/*
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);
});
});