import { describe, expect, it } from "vitest"; import { createRedirectLoopGuard, decideRouteAccess, } from "../../src/presentation/routes/navigation-policy.js"; import { NAVIGATION_ROUTES, ROUTE_REGISTRY, } from "../../src/contracts/routes.js"; describe("route registry", () => { it("matches the stable registry snapshot", () => { expect(ROUTE_REGISTRY).toMatchInlineSnapshot(` { "APP_HOME": { "access": "public", "chunkId": "route-home", "errorSurface": "route-boundary", "loadingSurface": "app-shell", "navigationLabel": "시작", "navigationOrder": 10, "paramsSchema": null, "path": "/", "routeId": "APP_HOME", "searchSchema": null, "title": "시작", }, "EXAMPLES_AUTH": { "access": "public", "chunkId": "route-examples-auth", "errorSurface": "route-boundary", "loadingSurface": "example-page", "navigationLabel": "인증 연동", "navigationOrder": 40, "paramsSchema": null, "path": "/examples/auth", "routeId": "EXAMPLES_AUTH", "searchSchema": null, "title": "인증 연동", }, "EXAMPLES_STATES": { "access": "public", "chunkId": "route-examples-states", "errorSurface": "route-boundary", "loadingSurface": "example-page", "navigationLabel": "화면 상태", "navigationOrder": 30, "paramsSchema": null, "path": "/examples/states", "routeId": "EXAMPLES_STATES", "searchSchema": null, "title": "화면 상태", }, "EXAMPLES_UI": { "access": "public", "chunkId": "route-examples-ui", "errorSurface": "route-boundary", "loadingSurface": "example-page", "navigationLabel": "UI 구성요소", "navigationOrder": 20, "paramsSchema": null, "path": "/examples/ui", "routeId": "EXAMPLES_UI", "searchSchema": null, "title": "UI 구성요소", }, "NOT_FOUND": { "access": "public", "chunkId": "route-not-found", "errorSurface": "not-found", "loadingSurface": "none", "navigationLabel": null, "navigationOrder": null, "paramsSchema": null, "path": "*", "routeId": "NOT_FOUND", "searchSchema": null, "title": "페이지를 찾을 수 없음", }, "SAMPLE_RESOURCE_LIST": { "access": "integration-defined", "chunkId": "route-sample-resources", "errorSurface": "feature-boundary", "loadingSurface": "sample-resource-list", "navigationLabel": "보호된 연동 지점", "navigationOrder": 50, "paramsSchema": null, "path": "/sample/resources", "routeId": "SAMPLE_RESOURCE_LIST", "searchSchema": "SampleResourceListQuery", "title": "보호된 연동 지점", }, } `); }); it("derives visible navigation in explicit order", () => { expect(NAVIGATION_ROUTES.map(({ routeId }) => routeId)).toEqual([ "APP_HOME", "EXAMPLES_UI", "EXAMPLES_STATES", "EXAMPLES_AUTH", "SAMPLE_RESOURCE_LIST", ]); }); it("treats client access as a UX hint, not authorization", () => { expect(decideRouteAccess("APP_HOME", "unauthenticated")).toEqual({ allowed: true, action: "none", }); expect(decideRouteAccess("SAMPLE_RESOURCE_LIST", "unauthenticated")).toEqual({ allowed: false, action: "show-sign-in", }); expect(decideRouteAccess("SAMPLE_RESOURCE_LIST", "authenticated")).toEqual({ allowed: true, action: "none", }); }); it("allows at most one automatic redirect per source-target pair", () => { const guard = createRedirectLoopGuard(); expect(guard.allow("/private", "/signin")).toBe(true); expect(guard.allow("/private", "/signin")).toBe(false); expect(guard.allow("/signin", "/signin")).toBe(false); }); });