import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import assert from "node:assert/strict"; import { execFileSync } from "node:child_process"; import { afterEach, describe, it } from "vitest"; import { buildInventory, hasInventoryIntegrityFailure, renderText } from "./verify-api-auth-surface.mjs"; const fixtures = []; function writeFile(rootDir, relativePath, content) { const absolutePath = path.join(rootDir, relativePath); fs.mkdirSync(path.dirname(absolutePath), { recursive: true }); fs.writeFileSync(absolutePath, content); } function createFixture() { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "api-auth-surface-")); fixtures.push(rootDir); return rootDir; } afterEach(() => { while (fixtures.length > 0) { fs.rmSync(fixtures.pop(), { recursive: true, force: true }); } }); describe("verify-api-auth-surface", () => { it("builds stable JSON inventory for apiService imports and callsites", async () => { const rootDir = createFixture(); writeFile( rootDir, "apps/api/src/entity-route.ts", `import { validateApiKey } from "~/utils/services/apiService"; export async function loader({ request, params }) { return validateApiKey(request, params); } `, ); writeFile( rootDir, "apps/api/src/usage-route.ts", `import { validateTenantApiKey, getEntityApiKeyFromRequest } from "~/utils/services/apiService"; export async function loader({ request, params }) { await validateTenantApiKey(request, params); return getEntityApiKeyFromRequest(request, params); } `, ); writeFile( rootDir, "apps/api/src/relationships-route.ts", `import { getRelationshipApiKeyFromRequest } from "~/utils/services/apiService"; export async function action({ request, params }) { return getRelationshipApiKeyFromRequest(request, params); } `, ); writeFile( rootDir, "apps/api/src/workflows-route.ts", `import { getWorkflowApiKeyFromRequest } from "~/utils/services/apiService"; export async function action({ request, params }) { return getWorkflowApiKeyFromRequest(request, params); } `, ); const inventory = await buildInventory(rootDir); assert.deepEqual(inventory.scannedFiles, [ "apps/api/src/entity-route.ts", "apps/api/src/relationships-route.ts", "apps/api/src/usage-route.ts", "apps/api/src/workflows-route.ts", ]); assert.deepEqual(inventory.scannedRoots, ["apps/api/src"]); assert.deepEqual(inventory.missingRoots, ["apps/web/app/routes/internal/api"]); assert.deepEqual(inventory.requiredMissingRoots, []); assert.equal(hasInventoryIntegrityFailure(inventory), false); assert.deepEqual( inventory.directValidateApiKeyCallsites.map((entry) => `${entry.file}:${entry.line}:${entry.symbol}`), ["apps/api/src/entity-route.ts:3:validateApiKey"], ); assert.deepEqual( inventory.tenantValidateCallsites.map((entry) => `${entry.file}:${entry.line}:${entry.symbol}`), ["apps/api/src/usage-route.ts:3:validateTenantApiKey"], ); assert.deepEqual( inventory.entityResolverCallsites.map((entry) => `${entry.file}:${entry.line}:${entry.symbol}`), ["apps/api/src/usage-route.ts:4:getEntityApiKeyFromRequest"], ); assert.deepEqual( inventory.relationshipHelperCallsites.map((entry) => `${entry.file}:${entry.line}:${entry.symbol}`), ["apps/api/src/relationships-route.ts:3:getRelationshipApiKeyFromRequest"], ); assert.deepEqual( inventory.workflowHelperCallsites.map((entry) => `${entry.file}:${entry.line}:${entry.symbol}`), ["apps/api/src/workflows-route.ts:3:getWorkflowApiKeyFromRequest"], ); assert.equal(inventory.apiServiceImports.length, 4); assert.equal(inventory.centralResolverFile, "apps/web/app/utils/services/apiService.ts"); assert.doesNotThrow(() => JSON.parse(JSON.stringify(inventory))); const text = renderText(inventory); assert.match(text, /API Auth Surface API auth surface inventory/); assert.match(text, /getRelationshipApiKeyFromRequest callsites/); assert.match(text, /getWorkflowApiKeyFromRequest callsites/); }); it("CLI exits 2 for unknown flags", () => { const scriptPath = path.resolve("scripts/verify-api-auth-surface.mjs"); assert.throws( () => execFileSync(process.execPath, [scriptPath, "--bogus"], { encoding: "utf8", stdio: "pipe" }), (error) => { assert.equal(error.status, 2); assert.match(error.stderr, /Unknown flag: --bogus/); return true; }, ); }); it("marks missing required API source roots as inventory failures", async () => { const rootDir = createFixture(); const inventory = await buildInventory(rootDir); assert.deepEqual(inventory.scannedFiles, []); assert.deepEqual(inventory.requiredMissingRoots, ["apps/api/src"]); assert.equal(hasInventoryIntegrityFailure(inventory), true); }); });