All checks were successful
CD / Build and push images (push) Successful in 2m36s
CI / Lint, typecheck, test (push) Successful in 1m50s
CI / Auth e2e pack (push) Successful in 1m58s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 9s
CD / Smoke tests against Test (push) Successful in 1m13s
CD / Promote to Int (push) Successful in 11s
Bootstrap apps/collab as a Hocuspocus WebSocket server (ADR 0003): - pino JSON logging (service=collab) and shared Zod env validation (collabEnvSchema); structured connection open/close logs. - /healthz endpoint (process liveness + PostgreSQL ping) served via the onRequest hook, matching the container-internal path and the proxied /collab/healthz path; any WebSocket handshake is accepted for now (authentication arrives with #34, persistence with #35). - Dockerfile (ESM workspace build) and a compose service on the frontend and internal networks with a healthcheck; dev overlay service and a new COLLAB_PORT variable. - CD builds, pushes, and promotes the collab image; CI builds it on PRs; the smoke suite asserts /collab/healthz through the reverse proxy. - deployment.md/stages.md: proxy routing, per-stage COLLAB_PORT, checklist. Closes #33 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
45 lines
1.7 KiB
TypeScript
45 lines
1.7 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import { buildHealthReport, isHealthRequest } from './health.js';
|
|
|
|
describe('buildHealthReport', () => {
|
|
it('reports ok with HTTP 200 when the database is reachable', () => {
|
|
const { httpStatus, body } = buildHealthReport('1.2.3', { ok: true });
|
|
expect(httpStatus).toBe(200);
|
|
expect(body.status).toBe('ok');
|
|
expect(body.service).toBe('collab');
|
|
expect(body.version).toBe('1.2.3');
|
|
expect(body.checks).toEqual([{ name: 'database', status: 'ok' }]);
|
|
expect(typeof body.time).toBe('string');
|
|
});
|
|
|
|
it('reports unhealthy with HTTP 503 and the detail when the database is down', () => {
|
|
const { httpStatus, body } = buildHealthReport('1.2.3', {
|
|
ok: false,
|
|
detail: 'connection refused',
|
|
});
|
|
expect(httpStatus).toBe(503);
|
|
expect(body.status).toBe('unhealthy');
|
|
expect(body.checks).toEqual([
|
|
{ name: 'database', status: 'failed', detail: 'connection refused' },
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('isHealthRequest', () => {
|
|
it('matches the internal and proxied health paths on GET/HEAD', () => {
|
|
expect(isHealthRequest('GET', '/healthz')).toBe(true);
|
|
expect(isHealthRequest('HEAD', '/healthz')).toBe(true);
|
|
expect(isHealthRequest('GET', '/healthz?probe=1')).toBe(true);
|
|
expect(isHealthRequest('GET', '/collab/healthz')).toBe(true);
|
|
expect(isHealthRequest('GET', '/collab/healthz/')).toBe(true);
|
|
});
|
|
|
|
it('does not match other methods, the WebSocket path, or the root', () => {
|
|
expect(isHealthRequest('POST', '/healthz')).toBe(false);
|
|
expect(isHealthRequest('GET', '/collab')).toBe(false);
|
|
expect(isHealthRequest('GET', '/')).toBe(false);
|
|
expect(isHealthRequest('GET', undefined)).toBe(false);
|
|
});
|
|
});
|