import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { NextRequest } from "next/server"; import { proxy } from "./proxy"; const request = (path: string, cookie = "mega_session=token") => new NextRequest(`http://app.test${path}`, { headers: cookie ? { cookie } : {} }); beforeEach(() => { vi.stubEnv("AUTH_MODE", "users"); vi.stubEnv("NEXT_PUBLIC_POCKETBASE_URL", "http://pb.test"); vi.stubGlobal("fetch", vi.fn(async () => Response.json({ record: { id: "u1", email: "u@example.test", role: "site_user", sites: ["site1"], is_active: true, } }))); }); afterEach(() => { vi.unstubAllGlobals(); vi.unstubAllEnvs(); }); describe("login proxy", () => { it("keeps requests protected when AUTH_MODE is absent or still set to pin", async () => { for (const mode of ["", "pin"]) { vi.stubEnv("AUTH_MODE", mode); expect((await proxy(request("/api/sites", ""))).status).toBe(401); } }); it("blocks site managers from every Management page and API", async () => { expect((await proxy(request("/dashboard/management/turnover"))).headers.get("location")).toBe("http://app.test/dashboard"); expect((await proxy(request("/api/management/turnover"))).status).toBe(403); }); it("rejects an anonymous data request", async () => { expect((await proxy(request("/api/sites", ""))).status).toBe(401); }); it("keeps clock users on clock pages and scan/upload APIs", async () => { vi.stubGlobal("fetch", vi.fn(async () => Response.json({ record: { id: "u1", email: "u@example.test", role: "clock_only", sites: ["site1"], is_active: true, } }))); expect((await proxy(request("/api/sites"))).status).toBe(403); expect((await proxy(request("/api/clock/scan"))).status).toBe(200); expect((await proxy(request("/api/delivery-notes/upload"))).status).toBe(200); expect((await proxy(request("/api/purchase-orders/upload"))).status).toBe(200); for (const path of ["/dashboard", "/dashboard/workforce", "/dashboard/financial", "/dashboard/plant", "/dashboard/settings/users"]) { expect((await proxy(request(path))).headers.get("location")).toBe("http://app.test/clock"); } for (const path of ["/api/timesheets/import", "/api/vehicles/vehicle", "/api/dn-batches/batch/notes/note/post", "/api/clock/events/event/correct"]) { expect((await proxy(request(path))).status).toBe(403); } }); it("limits a workforce office account to workforce pages and APIs", async () => { vi.stubGlobal("fetch", vi.fn(async () => Response.json({ record: { id: "office", email: "office@example.test", role: "office_user", modules: ["workforce"], all_sites: true, sites: [], is_active: true, } }))); for (const path of ["/dashboard/workforce", "/dashboard/workforce/workers", "/dashboard/workforce/new-feature", "/api/timesheets/import", "/api/employees/abcdefghijklmno/rates", "/api/employees/abcdefghijklmno/sick-notes", "/api/clock/events/event/correct"]) { expect((await proxy(request(path))).status, path).toBe(200); } for (const path of ["/dashboard", "/dashboard/sites", "/dashboard/plant", "/dashboard/financial", "/dashboard/chat", "/dashboard/settings/users", "/clock"]) { expect((await proxy(request(path))).headers.get("location"), path).toBe("http://app.test/dashboard/workforce"); } for (const path of ["/api/vehicles/vehicle", "/api/delivery-notes/upload", "/api/suppliers", "/api/admin/users", "/api/unknown"]) { expect((await proxy(request(path))).status, path).toBe(403); } }); it("blocks a site user from user administration and unknown APIs", async () => { expect((await proxy(request("/api/admin/users/another-user"))).status).toBe(403); expect((await proxy(request("/api/unknown"))).status).toBe(403); }); it("allows site managers into user creation but blocks clock users", async () => { expect((await proxy(request("/api/admin/users"))).status).toBe(200); expect((await proxy(request("/dashboard/settings/users"))).status).toBe(200); vi.stubGlobal("fetch", vi.fn(async () => Response.json({ record: { id: "u1", email: "u@example.test", role: "clock_only", sites: ["site1"], is_active: true, } }))); expect((await proxy(request("/api/admin/users"))).status).toBe(403); }); it("redirects a first-time user before rendering dashboard data", async () => { vi.stubGlobal("fetch", vi.fn(async () => Response.json({ record: { id: "u1", email: "u@example.test", role: "site_user", sites: ["site1"], is_active: true, must_change_password: true, } }))); const response = await proxy(request("/dashboard")); expect(response.status).toBe(307); expect(response.headers.get("location")).toBe("http://app.test/change-password"); }); it("preserves machine routes without a user cookie", async () => { expect((await proxy(request("/api/purchase-orders/ingest", ""))).status).toBe(200); expect(fetch).not.toHaveBeenCalled(); }); });