import { describe, expect, it, vi } from 'vitest'; import { buildApp } from '../../src/app.js'; import { loadConfig } from '../../src/config.js'; const config = loadConfig({ DATABASE_URL: 'postgres://localhost/staff', STANDALONE_TENANT_ID: '01J00000000000000000000000', STANDALONE_ACTOR_ID: '01J00000000000000000000001', LOG_LEVEL: 'silent', NODE_ENV: 'test', }); const database = { ping: vi.fn(async () => undefined), close: vi.fn(async () => undefined), } as never; describe('operational endpoints', () => { it('reports liveness, readiness and version without domain APIs', async () => { const app = await buildApp({ config, database }); expect((await app.inject('/api/v1/staff/live')).statusCode).toBe(200); expect((await app.inject('/api/v1/staff/ready')).statusCode).toBe(200); expect((await app.inject('/api/v1/staff/version')).json()).toMatchObject({ name: 'krona-staff', mode: 'standalone', }); expect((await app.inject('/live')).statusCode).toBe(404); expect((await app.inject('/api/v1/staff/not-a-route')).statusCode).toBe(404); await app.close(); }); it('returns 503 without leaking database errors', async () => { const unavailableDatabase = { ping: vi.fn(async () => { throw new Error('password=secret internal driver detail'); }), close: vi.fn(async () => undefined), } as never; const app = await buildApp({ config, database: unavailableDatabase }); const response = await app.inject('/api/v1/staff/ready'); expect(response.statusCode).toBe(503); expect(response.body).toBe('{"status":"unavailable"}'); expect(response.body).not.toContain('secret'); await app.close(); }); it('accepts only ULID correlation identifiers', async () => { const app = await buildApp({ config, database }); const valid = '01J00000000000000000000009'; expect( (await app.inject({ url: '/api/v1/staff/live', headers: { 'x-correlation-id': valid } })) .headers['x-correlation-id'], ).toBe(valid); const generated = ( await app.inject({ url: '/api/v1/staff/live', headers: { 'x-correlation-id': 'not-a-ulid'.repeat(100) }, }) ).headers['x-correlation-id']; expect(generated).toMatch(/^[0-9A-HJKMNP-TV-Z]{26}$/); await app.close(); }); });