import { readFile } from "node:fs/promises"; import { afterEach, describe, expect, it } from "vitest"; import { PublicApiAuthError, assertInternalWorkerToken, authenticatePublicApiRequest, getPublicApiClients, publicApiOwnerId, type PublicApiClient } from "@/lib/server/public-api-auth"; type AuthError = { status: number; message: string }; type HeaderMap = Record; type PublicApiAuthFixture = { version: 1; apiKeys: string; clients: PublicApiClient[]; authenticationCases: Array<{ name: string; headers: HeaderMap; expected?: { client: PublicApiClient; owner: string }; error?: AuthError; }>; ownerCases: Array<{ id: string; owner: string; maxPartLength?: number }>; workerCases: Array<{ name: string; production: boolean; configuredToken: string; headers: HeaderMap; allowed?: boolean; error?: AuthError; }>; }; const fixtureUrl = new URL("../contracts/auth/public-api-auth-v1.json", import.meta.url); const environmentKeys = ["ZHINIAN_API_KEYS", "ZHINIAN_INTERNAL_WORKER_TOKEN", "NODE_ENV"] as const; const originalEnvironment = new Map(environmentKeys.map((key) => [key, process.env[key]])); afterEach(() => { for (const key of environmentKeys) { const value = originalEnvironment.get(key); if (value === undefined) Reflect.deleteProperty(process.env, key); else Reflect.set(process.env, key, value); } }); async function loadFixture(): Promise { return JSON.parse(await readFile(fixtureUrl, "utf8")) as PublicApiAuthFixture; } function request(headers: HeaderMap): Request { return new Request("https://api.example.test/api/v1/generations", { headers }); } function expectTypedError(action: () => unknown, expected: AuthError) { try { action(); throw new Error("expected PublicApiAuthError"); } catch (error) { expect(error).toBeInstanceOf(PublicApiAuthError); expect(error).toMatchObject(expected); } } describe("public API authentication v1 cross-language contract", () => { it("freezes comma/newline parsing, trimming, default ids, and empty filtering", async () => { const fixture = await loadFixture(); process.env.ZHINIAN_API_KEYS = fixture.apiKeys; expect(fixture.version).toBe(1); expect(getPublicApiClients()).toEqual(fixture.clients); }); it("freezes public credential transport, precedence, errors, client, and owner", async () => { const fixture = await loadFixture(); process.env.ZHINIAN_API_KEYS = fixture.apiKeys; for (const testCase of fixture.authenticationCases) { if (testCase.error) { expectTypedError(() => authenticatePublicApiRequest(request(testCase.headers)), testCase.error); continue; } const client = authenticatePublicApiRequest(request(testCase.headers)); expect(client, testCase.name).toEqual(testCase.expected?.client); expect(publicApiOwnerId(client), testCase.name).toBe(testCase.expected?.owner); } }); it("sanitizes and bounds public owner ids", async () => { const fixture = await loadFixture(); for (const testCase of fixture.ownerCases) { const owner = publicApiOwnerId(testCase.id); expect(owner, testCase.id).toBe(testCase.owner); if (testCase.maxPartLength) expect(owner.slice(4)).toHaveLength(testCase.maxPartLength); } }); it("freezes internal worker development bypass and production failure semantics", async () => { const fixture = await loadFixture(); for (const testCase of fixture.workerCases) { Reflect.set(process.env, "NODE_ENV", testCase.production ? "production" : "development"); Reflect.set(process.env, "ZHINIAN_INTERNAL_WORKER_TOKEN", testCase.configuredToken); if (testCase.error) { expectTypedError(() => assertInternalWorkerToken(request(testCase.headers)), testCase.error); } else { expect(() => assertInternalWorkerToken(request(testCase.headers)), testCase.name).not.toThrow(); } } }); });