import { readFile } from 'node:fs/promises'; import { describe, expect, it, vi } from 'vitest'; import YAML from 'yaml'; import { buildApp } from '../../src/app.js'; import { loadConfig } from '../../src/config.js'; interface OpenApiDocument { servers: Array<{ url: string }>; paths: Record; } function assertOpenApiDocument(value: unknown): asserts value is OpenApiDocument { if ( typeof value !== 'object' || value === null || !('servers' in value) || !Array.isArray(value.servers) || !('paths' in value) || typeof value.paths !== 'object' || value.paths === null ) { throw new Error('api/openapi.yaml is not an OpenAPI document'); } } const config = loadConfig({ DATABASE_URL: 'postgres://localhost/staff', STANDALONE_TENANT_ID: '01ARZ3NDEKTSV4RRFFQ69G5FAV', STANDALONE_ACTOR_ID: '01J00000000000000000000001', LOG_LEVEL: 'silent', NODE_ENV: 'test', }); describe('OpenAPI runtime contract', () => { it('exposes the documented operational paths under the module prefix', async () => { const database = { ping: vi.fn(async () => undefined), close: vi.fn(async () => undefined), } as never; const app = await buildApp({ config, database }); await app.ready(); const documented: unknown = YAML.parse(await readFile('api/openapi.yaml', 'utf8')); assertOpenApiDocument(documented); const runtime = app.swagger(); const prefix = documented.servers[0]?.url; if (!prefix) throw new Error('OpenAPI server prefix is missing'); const documentedPaths = Object.keys(documented.paths).sort(); const runtimePaths = Object.keys(runtime.paths ?? {}) .filter((path) => path !== '/' && path !== '/openapi.json' && !path.startsWith('/staff')) .sort(); expect(runtimePaths).toEqual(documentedPaths); for (const path of documentedPaths) { const runtimePath = path .replace('{employeeId}', '01J00000000000000000000000') .replace('{workDate}', '2026-09-24') .replace('{runId}', '01J00000000000000000000000') .replace('{deviceId}', '01J00000000000000000000000'); const operations = documented.paths[path] as Record; const methods = ['get', 'post', 'put', 'patch', 'delete'] as const; const method = methods.find((candidate) => candidate in operations); if (!method) throw new Error(`No HTTP operation documented for ${path}`); const response = await app.inject({ method, url: `${prefix}${runtimePath}` }); expect(response.statusCode).not.toBe(404); } await app.close(); }); });