import { pino } from 'pino'; import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; import { createCollabServer } from './server.js'; import { freePort } from './testing/free-port.js'; import { InMemoryPagePersistence } from './testing/fake-persistence.js'; import type { CollabHealthReport, DatabaseProbe } from './health.js'; // A silent logger; these tests assert behaviour, not log output. const logger = pino({ enabled: false }); describe('collab server', () => { let server: ReturnType; let baseUrl: string; let wsUrl: string; const probe = vi.fn<() => Promise>(); beforeAll(async () => { server = createCollabServer({ version: 'test-version', logger, tokenSecret: 'server-test-secret-32-characters!', pingDatabase: probe, persistence: new InMemoryPagePersistence(), }); // Hocuspocus' listen(port) ignores a falsy port (0 → default 80), so bind // an explicit OS-assigned free port instead. const port = await freePort(); await server.listen(port); baseUrl = `http://127.0.0.1:${port}`; wsUrl = `ws://127.0.0.1:${port}`; }); afterAll(async () => { await server.destroy(); }); it('serves /healthz as 200 with the report when the database is reachable', async () => { probe.mockResolvedValueOnce({ ok: true }); const res = await fetch(`${baseUrl}/healthz`); expect(res.status).toBe(200); expect(res.headers.get('content-type')).toContain('application/json'); const body = (await res.json()) as CollabHealthReport; expect(body).toMatchObject({ status: 'ok', service: 'collab', version: 'test-version' }); expect(body.checks).toEqual([{ name: 'database', status: 'ok' }]); }); it('serves /healthz as 503 when the database probe fails', async () => { probe.mockResolvedValueOnce({ ok: false, detail: 'boom' }); const res = await fetch(`${baseUrl}/healthz`); expect(res.status).toBe(503); const body = (await res.json()) as CollabHealthReport; expect(body.status).toBe('unhealthy'); }); it('leaves non-health HTTP requests to Hocuspocus without probing the db', async () => { const callsBefore = probe.mock.calls.length; const res = await fetch(`${baseUrl}/`); expect(res.status).toBe(200); expect(await res.text()).toContain('Hocuspocus'); expect(probe.mock.calls.length).toBe(callsBefore); }); it('accepts a WebSocket handshake on the collab path', async () => { const socket = new WebSocket(`${wsUrl}/collab`); await expect( new Promise((resolve, reject) => { socket.addEventListener('open', () => resolve()); socket.addEventListener('error', () => reject(new Error('handshake failed'))); }), ).resolves.toBeUndefined(); socket.close(); }); });