Files
clean-architecture-frontend…/tests/unit/navigation-policy.test.ts
T
2026-08-01 19:39:59 +09:00

53 lines
1.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
NAVIGATION_ROUTES,
ROUTE_REGISTRY,
} from "../../src/features/installed-feature-contracts.ts";
import {
createRedirectLoopGuard,
decideRouteAccess,
} from "../../src/presentation/routes/navigation-policy.ts";
describe("installed route registry", () => {
it("derives visible navigation in explicit order", () => {
const expected = Object.values(ROUTE_REGISTRY)
.filter((route) => route.navigationOrder !== null)
.sort(
(left, right) =>
left.navigationOrder! - right.navigationOrder!,
)
.map((route) => route.routeId);
expect(NAVIGATION_ROUTES.map(({ routeId }) => routeId)).toEqual(expected);
});
it("allows public routes without consulting a product permission", () => {
expect(decideRouteAccess("APP_HOME", "unauthenticated")).toEqual({
allowed: true,
action: "none",
});
});
it("treats every non-public route as explicitly session-required", () => {
expect(ROUTE_REGISTRY.REFERENCE_RESOURCE_LIST.access).toBe(
"session-required",
);
expect(
decideRouteAccess("REFERENCE_RESOURCE_LIST", "unauthenticated"),
).toEqual({ allowed: false, action: "show-sign-in" });
});
it("bounds automatic redirects by pair and maximum hops", () => {
const guard = createRedirectLoopGuard(2);
expect(guard.allow("/private", "/signin")).toBe(true);
expect(guard.allow("/private", "/signin")).toBe(false);
expect(guard.allow("/signin", "/signin")).toBe(false);
expect(guard.allow("/signin", "/continue")).toBe(true);
expect(guard.allow("/continue", "/final")).toBe(false);
expect(guard.hopCount).toBe(2);
guard.reset();
expect(guard.allow("/unrelated", "/canonical-unrelated")).toBe(true);
});
});