// ABOUTME: Unit tests for Next.js middleware page protection. // ABOUTME: Tests that protected routes redirect to login when unauthenticated. import type { NextRequest } from "next/server"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { config, middleware } from "./middleware"; describe("middleware", () => { beforeEach(() => { vi.clearAllMocks(); }); function createMockRequest(url: string, cookieValue?: string): NextRequest { const request = { nextUrl: new URL(url, "http://localhost:3000"), url: `http://localhost:3000${url}`, cookies: { get: vi .fn() .mockReturnValue( cookieValue ? { name: "pb_auth", value: cookieValue } : undefined, ), }, } as unknown as NextRequest; return request; } describe("protected routes", () => { it("redirects to /login when pb_auth cookie is missing", async () => { const request = createMockRequest("/"); const response = await middleware(request); expect(response.status).toBe(307); expect(response.headers.get("location")).toBe( "http://localhost:3000/login", ); }); it("redirects to /login when pb_auth cookie is empty", async () => { const request = createMockRequest("/", ""); const response = await middleware(request); expect(response.status).toBe(307); expect(response.headers.get("location")).toBe( "http://localhost:3000/login", ); }); it("allows access when pb_auth cookie is present", async () => { const request = createMockRequest("/", "valid-token"); const response = await middleware(request); expect(response.status).toBe(200); }); it("redirects /settings to /login when unauthenticated", async () => { const request = createMockRequest("/settings"); const response = await middleware(request); expect(response.status).toBe(307); expect(response.headers.get("location")).toBe( "http://localhost:3000/login", ); }); it("redirects /calendar to /login when unauthenticated", async () => { const request = createMockRequest("/calendar"); const response = await middleware(request); expect(response.status).toBe(307); expect(response.headers.get("location")).toBe( "http://localhost:3000/login", ); }); }); describe("public routes", () => { it("allows /login without authentication", async () => { const request = createMockRequest("/login"); const response = await middleware(request); expect(response.status).toBe(200); }); it("allows /login even with authentication cookie", async () => { const request = createMockRequest("/login", "valid-token"); const response = await middleware(request); // Could redirect authenticated users away from login, but for now just allow expect(response.status).toBe(200); }); }); describe("API routes", () => { it("skips middleware for /api routes (handled by route-level auth)", async () => { const request = createMockRequest("/api/user"); const response = await middleware(request); // API routes are not processed by this middleware expect(response.status).toBe(200); }); it("skips middleware for /api/calendar ICS endpoint", async () => { const request = createMockRequest("/api/calendar/user123/token.ics"); const response = await middleware(request); expect(response.status).toBe(200); }); }); describe("static assets", () => { it("skips middleware for _next static assets", async () => { const request = createMockRequest("/_next/static/chunk.js"); const response = await middleware(request); expect(response.status).toBe(200); }); it("skips middleware for favicon", async () => { const request = createMockRequest("/favicon.ico"); const response = await middleware(request); expect(response.status).toBe(200); }); }); }); describe("middleware config", () => { it("exports a matcher configuration", () => { expect(config).toBeDefined(); expect(config.matcher).toBeDefined(); expect(Array.isArray(config.matcher)).toBe(true); }); });