krona-staff/tests/integration/health.test.ts

62 lines
2.3 KiB
TypeScript

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();
});
});