import { describe, expect, it } from "vitest"; import { PUBLIC_ALLOWLIST, filePathToRoute, findUndeclaredRoutes, discoverAppRoutes, } from "../support/route-scan"; import { DECLARED_ROUTE_TEMPLATES, ROUTES, } from "../e2e/routes.manifest"; /** * Driftschutz für das Routen-Manifest (Definition of Done #1): jede neue Route * unter src/app/** muss im Manifest geführt (oder explizit öffentlich) sein, * sonst bleibt sie ungetestet im Auth-Gating. Reine Logik, offline lauffähig. */ describe("filePathToRoute", () => { it("mappt page.tsx einer Route-Group auf den URL-Pfad ohne Gruppe", () => { expect(filePathToRoute("src/app/(app)/fahrzeuge/page.tsx")).toBe( "/fahrzeuge", ); }); it("mappt die Wurzel-Page der (app)-Gruppe auf /", () => { expect(filePathToRoute("src/app/(app)/page.tsx")).toBe("/"); }); it("ersetzt dynamische Segmente durch einen Platzhalter", () => { expect(filePathToRoute("src/app/(app)/fahrzeuge/[id]/page.tsx")).toBe( "/fahrzeuge/[id]", ); }); it("mappt route.ts auf den API-Pfad", () => { expect(filePathToRoute("src/app/api/health/route.ts")).toBe("/api/health"); }); it("mappt die Root-Page src/app/page.tsx auf /", () => { expect(filePathToRoute("src/app/page.tsx")).toBe("/"); }); it("löst catch-all-Segmente auf", () => { expect(filePathToRoute("src/app/api/auth/[...nextauth]/route.ts")).toBe( "/api/auth/[...nextauth]", ); }); }); describe("findUndeclaredRoutes", () => { it("flaggt eine Route, die weder im Manifest noch öffentlich ist", () => { const declared = new Set(["/fahrzeuge"]); const discovered = ["/fahrzeuge", "/leak"]; expect(findUndeclaredRoutes(discovered, declared)).toEqual(["/leak"]); }); it("ignoriert Routen mit öffentlichem Präfix", () => { const declared = new Set(); const discovered = ["/login", "/api/health", "/api/auth/[...nextauth]"]; expect(findUndeclaredRoutes(discovered, declared)).toEqual([]); }); it("flaggt Routen mit reinem String-Präfix als ungetestet (kein Pfadsegment)", () => { const declared = new Set(); // "/loginhelp" beginnt zwar mit "/login" und "/api/healthz" mit // "/api/health", sind aber KEINE Unterpfade -> müssen gegated werden. const discovered = ["/loginhelp", "/api/healthz", "/api/authentication"]; expect(findUndeclaredRoutes(discovered, declared)).toEqual([ "/loginhelp", "/api/healthz", "/api/authentication", ]); }); it("PUBLIC_ALLOWLIST enthält /api/health und /login", () => { expect(PUBLIC_ALLOWLIST).toContain("/api/health"); expect(PUBLIC_ALLOWLIST).toContain("/login"); }); }); describe("discoverAppRoutes (echtes Repo)", () => { it("findet die bekannten Seiten", () => { const routes = discoverAppRoutes(); expect(routes).toContain("/fahrzeuge"); expect(routes).toContain("/admin"); expect(routes).toContain("/api/health"); }); }); describe("Driftschutz: Manifest deckt alle Routen ab (Definition of Done #1)", () => { it("KEINE Route unter src/app/** fehlt im Manifest oder in der Allowlist", () => { const discovered = discoverAppRoutes(); const undeclared = findUndeclaredRoutes( discovered, DECLARED_ROUTE_TEMPLATES, ); expect( undeclared, `Ungetestete Routen (im Manifest ergänzen oder als öffentlich markieren):\n${undeclared.join( "\n", )}`, ).toEqual([]); }); it("jeder ROUTES-Eintrag entspricht einer existierenden Route-Vorlage", () => { const discovered = new Set(discoverAppRoutes()); for (const template of DECLARED_ROUTE_TEMPLATES) { expect(discovered, `Manifest-Eintrag ${template} fehlt im Dateisystem`).toContain( template, ); } // Sanity: jede konkrete ROUTES-Zeile ist nicht leer. for (const r of ROUTES) expect(r.path.startsWith("/")).toBe(true); }); });