import { describe, expect, it } from "vitest"; import { TECH_LOG_ROUTE_REGISTRY, TECH_LOG_ROUTE_RUNTIME_CONTRACT, } from "../../../src/features/tech-log/contracts/tech-log-route-contract.ts"; import { TECH_LOG_MESSAGE_CATALOGS } from "../../../src/features/tech-log/contracts/tech-log-message-catalog.ts"; import { TECH_LOG_ROUTE_CODECS } from "../../../src/features/tech-log/presentation/tech-log-route-codecs.ts"; import { ROUTE_REGISTRY } from "../../../src/features/installed-feature-contracts.ts"; import { ROUTE_RUNTIME } from "../../../src/features/installed-feature-runtimes.tsx"; import { buildRouteUrlFromContract } from "../../../src/presentation/routes/route-codecs.ts"; import { PLATFORM_ROUTE_CODECS } from "../../../src/presentation/routes/platform-route-codecs.ts"; const expectedRoutes = [ ["TECH_LOG_HOME", "/", "PUBLIC", null, "TechLogHomeSearch"], ["TECH_LOG_EXPLORE", "/explore", "PUBLIC", null, "TechLogExploreSearch"], ["TECH_LOG_EXPLORE_KIND", "/explore/:kind", "PUBLIC", "TechLogExploreKindParams", "TechLogExploreKindSearch"], ["TECH_LOG_CASE", "/cases/:slug", "PUBLIC", "TechLogSlugParams", "TechLogCaseStateSearch"], ["TECH_LOG_REFERENCE", "/references/:slug", "PUBLIC", "TechLogSlugParams", null], ["TECH_LOG_QUESTION", "/questions/:slug", "PUBLIC", "TechLogSlugParams", null], ["TECH_LOG_TOPIC", "/topics/:slug", "PUBLIC", "TechLogSlugParams", null], ["TECH_LOG_PROJECTS", "/projects", "PUBLIC", null, null], ["TECH_LOG_PROJECT", "/projects/:slug", "PUBLIC", "TechLogSlugParams", null], ["TECH_LOG_PROJECT_RECORDS", "/projects/:slug/records", "PUBLIC", "TechLogSlugParams", null], ["TECH_LOG_PROJECT_DECISIONS", "/projects/:slug/decisions", "PUBLIC", "TechLogSlugParams", null], ["TECH_LOG_PROJECT_ACTIVITY", "/projects/:slug/activity", "PUBLIC", "TechLogSlugParams", null], ["TECH_LOG_RELEASES", "/releases", "PUBLIC", null, null], ["TECH_LOG_RELEASE", "/releases/:version", "PUBLIC", "TechLogVersionParams", null], ["TECH_LOG_PROFILE", "/profile", "PUBLIC", null, null], ["TECH_LOG_SEARCH", "/search", "PUBLIC", null, "TechLogSearchQuery"], ["TECH_LOG_STUDIO_HOME", "/studio", "STUDIO", null, null], ["TECH_LOG_STUDIO_DOCUMENTS", "/studio/documents", "STUDIO", null, null], ["TECH_LOG_STUDIO_DOCUMENT_NEW", "/studio/documents/new", "STUDIO", null, null], ["TECH_LOG_STUDIO_DOCUMENT_EDIT", "/studio/documents/:id/edit", "STUDIO", "TechLogDocumentIdParams", null], ["TECH_LOG_STUDIO_DOCUMENT_VALIDATION", "/studio/documents/:id/validation", "STUDIO", "TechLogDocumentIdParams", null], ["TECH_LOG_STUDIO_DOCUMENT_PREVIEW", "/studio/documents/:id/preview", "STUDIO", "TechLogDocumentIdParams", null], ["TECH_LOG_STUDIO_DOCUMENT_PUBLISH", "/studio/documents/:id/publish", "STUDIO", "TechLogDocumentIdParams", null], ["TECH_LOG_STUDIO_PUBLICATIONS", "/studio/publications", "STUDIO", null, null], ["TECH_LOG_STUDIO_PUBLICATION_PREVIEW", "/studio/publications/:publicationEventId/preview", "STUDIO", "TechLogPublicationEventIdParams", null], ["TECH_LOG_STUDIO_NOT_FOUND", "/studio/*", "STUDIO", "TechLogStudioSplat", null], ["NOT_FOUND", "*", "PUBLIC", "NotFoundSplat", null], ] as const; const expectedTitles = { TECH_LOG_HOME: "TechLog", TECH_LOG_EXPLORE: "탐색", TECH_LOG_EXPLORE_KIND: "유형별 탐색", TECH_LOG_CASE: "Case", TECH_LOG_REFERENCE: "Reference", TECH_LOG_QUESTION: "Open Question", TECH_LOG_TOPIC: "Topic", TECH_LOG_PROJECTS: "프로젝트", TECH_LOG_PROJECT: "프로젝트", TECH_LOG_PROJECT_RECORDS: "프로젝트 기록", TECH_LOG_PROJECT_DECISIONS: "프로젝트 결정", TECH_LOG_PROJECT_ACTIVITY: "프로젝트 활동", TECH_LOG_RELEASES: "변경 기록", TECH_LOG_RELEASE: "변경 기록", TECH_LOG_PROFILE: "프로필", TECH_LOG_SEARCH: "검색", TECH_LOG_STUDIO_HOME: "TechLog Studio", TECH_LOG_STUDIO_DOCUMENTS: "작업본", TECH_LOG_STUDIO_DOCUMENT_NEW: "새 문서", TECH_LOG_STUDIO_DOCUMENT_EDIT: "문서 편집", TECH_LOG_STUDIO_DOCUMENT_VALIDATION: "문서 검증", TECH_LOG_STUDIO_DOCUMENT_PREVIEW: "Public Preview", TECH_LOG_STUDIO_DOCUMENT_PUBLISH: "게시", TECH_LOG_STUDIO_PUBLICATIONS: "게시 기록", TECH_LOG_STUDIO_PUBLICATION_PREVIEW: "게시 Snapshot", TECH_LOG_STUDIO_NOT_FOUND: "Studio 화면을 찾을 수 없습니다", NOT_FOUND: "페이지를 찾을 수 없습니다.", } as const; describe("TechLog route boundary contract", () => { it("freezes the standalone 27-route inventory before runtime installation", () => { expect( Object.values(TECH_LOG_ROUTE_REGISTRY).map((definition) => [ definition.routeId, definition.path, definition.layoutGroup, definition.paramsSchema, definition.searchSchema, ]), ).toEqual(expectedRoutes); expect( Object.values(TECH_LOG_ROUTE_REGISTRY).map((definition) => [ definition.routeId, definition.access, definition.chunkId, ]), ).toEqual( expectedRoutes.map(([routeId]) => [ routeId, "public", routeId === "NOT_FOUND" ? "route-not-found" : `route-${routeId.toLowerCase().replaceAll("_", "-")}`, ]), ); }); it("keeps runtime module and codec bindings aligned with every dormant route", () => { expect(Object.keys(TECH_LOG_ROUTE_RUNTIME_CONTRACT)).toEqual( expectedRoutes.map(([routeId]) => routeId), ); for (const [routeId, , , paramsSchema, searchSchema] of expectedRoutes) { expect(TECH_LOG_ROUTE_RUNTIME_CONTRACT[routeId]).toEqual({ routeId, moduleId: routeId === "NOT_FOUND" ? "route-not-found" : `route-${routeId.toLowerCase().replaceAll("_", "-")}`, paramsCodec: paramsSchema ?? "none", searchCodec: searchSchema ?? "none", }); } }); it("preserves source navigation labels and the complete route message catalog", () => { expect( Object.values(TECH_LOG_ROUTE_REGISTRY) .filter((definition) => definition.navigationLabel !== null) .map(({ routeId, layoutGroup, navigationLabel, navigationOrder }) => [ routeId, layoutGroup, navigationLabel, navigationOrder, ]), ).toEqual([ ["TECH_LOG_EXPLORE", "PUBLIC", "탐색", 10], ["TECH_LOG_PROJECTS", "PUBLIC", "프로젝트", 20], ["TECH_LOG_RELEASES", "PUBLIC", "변경 기록", 30], ["TECH_LOG_PROFILE", "PUBLIC", "프로필", 40], ["TECH_LOG_STUDIO_DOCUMENTS", "STUDIO", "작업본", 10], ["TECH_LOG_STUDIO_DOCUMENT_NEW", "STUDIO", "새 문서", 30], ["TECH_LOG_STUDIO_PUBLICATIONS", "STUDIO", "게시 기록", 20], ]); for (const locale of ["ko-KR", "en-US"] as const) { const catalog: Readonly> = TECH_LOG_MESSAGE_CATALOGS[locale]; expect(Object.keys(catalog)).toHaveLength(54); for (const [routeId, title] of Object.entries(expectedTitles)) { expect(catalog[`route.${routeId}.title`]).toBe(title); expect(catalog[`route.${routeId}.navigation`]).toBe(title); } } }); it("normalizes source search semantics and rejects empty path parameters", () => { expect( TECH_LOG_ROUTE_CODECS.TechLogExploreSearch.parse({ type: ["CASE", "REFERENCE"], topic: [" JPA ", "Redis"], project: " tech-log ", unknown: "drop-me", }), ).toEqual({ type: "CASE", topic: "JPA", project: "tech-log" }); expect( TECH_LOG_ROUTE_CODECS.TechLogHomeSearch.parse({ focus: ["architecture", "delivery"], state: ["latest-empty", "site-error"], }), ).toEqual({ focus: "architecture", state: "latest-empty" }); expect( TECH_LOG_ROUTE_CODECS.TechLogSearchQuery.parse({ q: [" gateway ", "ignored"] }), ).toEqual({ q: "gateway" }); expect( TECH_LOG_ROUTE_CODECS.TechLogSlugParams.safeParse({ slug: "" }).success, ).toBe(false); expect( TECH_LOG_ROUTE_CODECS.TechLogDocumentIdParams.safeParse({ id: "" }).success, ).toBe(false); }); it("builds canonical TechLog URLs with non-empty string codecs", () => { const route = TECH_LOG_ROUTE_REGISTRY.TECH_LOG_STUDIO_PUBLICATION_PREVIEW; const runtime = TECH_LOG_ROUTE_RUNTIME_CONTRACT.TECH_LOG_STUDIO_PUBLICATION_PREVIEW; expect( buildRouteUrlFromContract(route, runtime, { ...PLATFORM_ROUTE_CODECS, ...TECH_LOG_ROUTE_CODECS, }, { params: { publicationEventId: "event/01" }, }), ).toBe("/studio/publications/event%2F01/preview"); expect(() => buildRouteUrlFromContract(route, runtime, { ...PLATFORM_ROUTE_CODECS, ...TECH_LOG_ROUTE_CODECS, }, { params: { publicationEventId: "" }, }), ).toThrow(); }); it("atomically installs the complete TechLog route and runtime inventories", () => { expect(Object.keys(ROUTE_REGISTRY)).toEqual( expectedRoutes.map(([routeId]) => routeId), ); expect(Object.keys(ROUTE_RUNTIME)).toEqual(Object.keys(ROUTE_REGISTRY)); expect(ROUTE_REGISTRY).toEqual(TECH_LOG_ROUTE_REGISTRY); }); });