import { describe, it, expect, beforeAll, afterAll, afterEach, vi } from 'vitest'; import Fastify from 'fastify'; import type { FastifyInstance } from 'fastify'; import { SignJWT } from 'jose'; import { config } from './lib/config.js'; import { productConfig } from './lib/product-config.js'; import { assertDiagnosticsAccess, diagnosticsRoutes } from './lib/diagnostics-routes.js'; let app: FastifyInstance; beforeAll(async () => { app = Fastify({ logger: false }); await diagnosticsRoutes(app); app.get('/health', async (req) => ({ status: 'ok', service: config.SERVICE_NAME, version: '0.1.0', timestamp: new Date().toISOString(), requestId: req.id, })); app.get('/api/bootstrap', async () => ({ productId: productConfig.productId, displayName: productConfig.displayName, backendPort: config.PORT, })); await app.ready(); }); afterAll(async () => { await app.close(); }); afterEach(() => { vi.unstubAllGlobals(); }); function makeToken(role: string) { return new SignJWT({ sub: 'diagnostics-user', role, type: 'access', }) .setProtectedHeader({ alg: 'HS256' }) .setIssuedAt() .setExpirationTime('5m') .sign(new TextEncoder().encode(config.JWT_SECRET)); } describe('diagnostics routes', () => { it('GET /api/diagnostics/flags returns feature flags', async () => { const res = await app.inject({ method: 'GET', url: '/api/diagnostics/flags' }); expect(res.statusCode).toBe(200); const body = res.json(); expect(typeof body).toBe('object'); expect(body).not.toBeNull(); }); it('GET /api/diagnostics/telemetry returns buffered events', async () => { const res = await app.inject({ method: 'GET', url: '/api/diagnostics/telemetry' }); expect(res.statusCode).toBe(200); const body = res.json(); expect(body).toHaveProperty('events'); expect(Array.isArray(body.events)).toBe(true); }); it('POST /api/diagnostics/telemetry/flush returns flushed count', async () => { const res = await app.inject({ method: 'POST', url: '/api/diagnostics/telemetry/flush' }); expect(res.statusCode).toBe(200); const body = res.json(); expect(body).toHaveProperty('flushed'); expect(typeof body.flushed).toBe('number'); }); it('GET /api/diagnostics/config returns sanitized config', async () => { const res = await app.inject({ method: 'GET', url: '/api/diagnostics/config' }); expect(res.statusCode).toBe(200); const body = res.json(); expect(body).toHaveProperty('productId'); expect(body).toHaveProperty('serviceName'); expect(body).toHaveProperty('port'); expect(body).toHaveProperty('nodeEnv'); expect(body).toHaveProperty('dbProvider'); expect(typeof body.telemetryEnabled).toBe('boolean'); expect(typeof body.featureFlagsEnabled).toBe('boolean'); }); it('GET /api/diagnostics/readiness returns dependency health summary', async () => { vi.stubGlobal( 'fetch', vi.fn(async () => new Response(JSON.stringify({ status: 'ok' }), { status: 200 })), ); const res = await app.inject({ method: 'GET', url: '/api/diagnostics/readiness' }); expect(res.statusCode).toBe(200); const body = res.json(); expect(body.overall).toBe('ready'); expect(body.summary).toMatchObject({ total: 5, unhealthy: 0, unreachable: 0 }); expect(body.dependencies.map((dep: { name: string }) => dep.name)).toEqual([ 'datastore', 'encryption', 'platform-service', 'extraction-service', 'mcp', ]); }); it('GET /health returns standard health response', async () => { const res = await app.inject({ method: 'GET', url: '/health' }); expect(res.statusCode).toBe(200); const body = res.json(); expect(body.status).toBe('ok'); expect(body).toHaveProperty('service'); expect(body).toHaveProperty('version'); expect(body).toHaveProperty('timestamp'); expect(body.timestamp).toMatch(/^\d{4}-\d{2}-\d{2}T/); }); it('GET /api/bootstrap returns product identity', async () => { const res = await app.inject({ method: 'GET', url: '/api/bootstrap' }); expect(res.statusCode).toBe(200); const body = res.json(); expect(body).toHaveProperty('productId'); expect(body).toHaveProperty('displayName'); expect(body).toHaveProperty('backendPort'); expect(typeof body.productId).toBe('string'); expect(typeof body.displayName).toBe('string'); expect(typeof body.backendPort).toBe('number'); }); it('keeps diagnostics open outside production for local smoke ergonomics', async () => { await expect(assertDiagnosticsAccess({ headers: {} }, 'development')).resolves.toBeUndefined(); await expect(assertDiagnosticsAccess({ headers: {} }, 'test')).resolves.toBeUndefined(); }); it('requires auth for diagnostics in production', async () => { await expect(assertDiagnosticsAccess({ headers: {} }, 'production')).rejects.toThrow('Unauthorized'); }); it('allows production diagnostics for admin or owner roles only', async () => { const adminToken = await makeToken('admin'); const ownerToken = await makeToken('owner'); const viewerToken = await makeToken('viewer'); await expect( assertDiagnosticsAccess({ headers: { authorization: `Bearer ${adminToken}` } }, 'production') ).resolves.toBeUndefined(); await expect( assertDiagnosticsAccess({ headers: { authorization: `Bearer ${ownerToken}` } }, 'production') ).resolves.toBeUndefined(); await expect( assertDiagnosticsAccess({ headers: { authorization: `Bearer ${viewerToken}` } }, 'production') ).rejects.toThrow('Insufficient permissions'); }); });