/* RFC-0899: Unit tests for access protection middleware logic — constant-time comparison, host matching, PIN gating. Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only. Fixtures stay inline and minimal so each case states its own setup. RFC-0899: Initial middleware unit tests. RFC-1249: wire werkstatt.commands.validate into packages.check + retire CMD-OUTPUT debt RFC-0899: protect workers.dev dev and alt channels with the PIN and noindex RFC-1325: provisional workers.dev as first-class site host Single host predicate module in werkstatt-shared; provisional registration derives dev/alt/main workers.dev channels from the account subdomain; first dev deploy auto-protects provisional hosts; provisional main responses carry noindex; dns-aid skips provisional sites; site.domain.adopt cuts over to a custom domain. */ // @ai-invariant fixtures must not couple to internals beyond the public surface under test import { describe, expect, it, vi } from "vitest"; const mockEnv: Record = {}; vi.mock("cloudflare:workers", () => ({ env: mockEnv, })); vi.mock("astro:middleware", () => ({ defineMiddleware: (handler: unknown) => handler, })); describe("RFC-0899: access protection middleware", () => { async function loadMiddleware() { return (await import("../access-protection.ts")).accessProtectionMiddleware as ( context: unknown, next: () => Promise, ) => Promise; } function makeContext(host: string, authHeader?: string, pathname = "/") { const headers = new Map(); headers.set("host", host); if (authHeader) headers.set("authorization", authHeader); return { request: { headers: { get: (name: string) => headers.get(name.toLowerCase()) ?? null, }, url: `https://${host}${pathname}`, }, }; } async function runMiddleware( handler: (context: unknown, next: () => Promise) => Promise, host: string, authHeader?: string, pathname = "/", ): Promise { let nextCalled = false; const nextResponse = new Response("page content", { status: 200 }); const result = await handler(makeContext(host, authHeader, pathname), async () => { nextCalled = true; return nextResponse; }); return Object.assign(result, { _nextCalled: nextCalled }); } it("passes through for main domain (no dev/alt prefix)", async () => { mockEnv.ACCESS_PIN = "1234"; const handler = await loadMiddleware(); const res = await runMiddleware(handler, "example.com"); expect(res._nextCalled).toBe(true); expect(res.status).toBe(200); }); it("returns 401 for dev.* without auth header when PIN is set", async () => { mockEnv.ACCESS_PIN = "1234"; const handler = await loadMiddleware(); const res = await runMiddleware(handler, "dev.example.com"); expect(res._nextCalled).toBe(false); expect(res.status).toBe(401); expect(res.headers.get("WWW-Authenticate")).toBe('Basic realm="Staging Access"'); expect(res.headers.get("X-Robots-Tag")).toBe("noindex, nofollow, noai, noimageai"); }); it("returns 401 for alt.* without auth header when PIN is set", async () => { mockEnv.ACCESS_PIN = "1234"; const handler = await loadMiddleware(); const res = await runMiddleware(handler, "alt.example.com"); expect(res._nextCalled).toBe(false); expect(res.status).toBe(401); }); it("returns 401 for a workers.dev dev channel (dev-..workers.dev)", async () => { mockEnv.ACCESS_PIN = "1234"; const handler = await loadMiddleware(); const res = await runMiddleware(handler, "dev-dp7.syrokomskyi.workers.dev"); expect(res._nextCalled).toBe(false); expect(res.status).toBe(401); }); it("returns 401 for a workers.dev alt channel", async () => { mockEnv.ACCESS_PIN = "1234"; const handler = await loadMiddleware(); const res = await runMiddleware(handler, "alt-dp7.syrokomskyi.workers.dev"); expect(res.status).toBe(401); }); it("passes through the workers.dev main channel and look-alike hosts", async () => { mockEnv.ACCESS_PIN = "1234"; const handler = await loadMiddleware(); expect((await runMiddleware(handler, "dp7.syrokomskyi.workers.dev"))._nextCalled).toBe(true); expect((await runMiddleware(handler, "devtools.example.com"))._nextCalled).toBe(true); }); it("RFC-1325: noindexes the unprotected workers.dev main channel (public preview, never indexed)", async () => { mockEnv.ACCESS_PIN = "1234"; const handler = await loadMiddleware(); const res = await runMiddleware(handler, "dp7.syrokomskyi.workers.dev"); expect(res._nextCalled).toBe(true); expect(res.headers.get("X-Robots-Tag")).toBe("noindex, nofollow, noai, noimageai"); // A real custom domain still carries no noindex. const prod = await runMiddleware(handler, "dp7.example.com"); expect(prod.headers.get("X-Robots-Tag")).toBe(null); }); it("passes through dev.* with correct Basic Auth", async () => { mockEnv.ACCESS_PIN = "1234"; const expected = `Basic ${btoa("warp:1234")}`; const handler = await loadMiddleware(); const res = await runMiddleware(handler, "dev.example.com", expected); expect(res._nextCalled).toBe(true); expect(res.status).toBe(200); expect(res.headers.get("X-Robots-Tag")).toBe("noindex, nofollow, noai, noimageai"); }); it("returns 401 for dev.* with wrong PIN", async () => { mockEnv.ACCESS_PIN = "1234"; const wrong = `Basic ${btoa("warp:9999")}`; const handler = await loadMiddleware(); const res = await runMiddleware(handler, "dev.example.com", wrong); expect(res._nextCalled).toBe(false); expect(res.status).toBe(401); }); it("passes through dev.* when PIN is not set (no env var)", async () => { delete mockEnv.ACCESS_PIN; const handler = await loadMiddleware(); const res = await runMiddleware(handler, "dev.example.com"); expect(res._nextCalled).toBe(true); expect(res.status).toBe(200); expect(res.headers.get("X-Robots-Tag")).toBe("noindex, nofollow, noai, noimageai"); }); it("passes through alt.* when PIN env is undefined", async () => { mockEnv.ACCESS_PIN = undefined; const handler = await loadMiddleware(); const res = await runMiddleware(handler, "alt.example.com"); expect(res._nextCalled).toBe(true); expect(res.headers.get("X-Robots-Tag")).toBe("noindex, nofollow, noai, noimageai"); }); it("does not set X-Robots-Tag on main domain", async () => { mockEnv.ACCESS_PIN = "1234"; const handler = await loadMiddleware(); const res = await runMiddleware(handler, "example.com"); expect(res.headers.get("X-Robots-Tag")).toBe(null); }); it("exempts /manifest.webmanifest from auth on dev.* when PIN is set", async () => { mockEnv.ACCESS_PIN = "1234"; const handler = await loadMiddleware(); const res = await runMiddleware(handler, "dev.example.com", undefined, "/manifest.webmanifest"); expect(res._nextCalled).toBe(true); expect(res.status).toBe(200); expect(res.headers.get("X-Robots-Tag")).toBe("noindex, nofollow, noai, noimageai"); }); it("exempts /manifest.webmanifest from auth on alt.* when PIN is set", async () => { mockEnv.ACCESS_PIN = "1234"; const handler = await loadMiddleware(); const res = await runMiddleware(handler, "alt.example.com", undefined, "/manifest.webmanifest"); expect(res._nextCalled).toBe(true); expect(res.status).toBe(200); }); });