import assert from 'node:assert/strict'; import type { AddressInfo } from 'node:net'; import test from 'node:test'; import type { Pool } from 'pg'; import { createApp } from '../src/app.js'; import { loadConfig } from '../src/config/env.js'; const config = loadConfig({ NODE_ENV: 'test', HOST: '127.0.0.1', PORT: '4400', DATABASE_URL: 'postgres://runtime:password@127.0.0.1:5432/saas_voc', PARSE_APP_ID: 'test-app-id', PARSE_MASTER_KEY: 'a'.repeat(32), PARSE_MAINTENANCE_KEY: 'b'.repeat(32), PARSE_SERVER_URL: 'http://127.0.0.1:4400/parse', API_AUTH_MODE: 'disabled', FMODE_BASE_URL: 'http://127.0.0.1:3000/api/voc-e-commerce', FMODE_API_KEY: 'test-key', CORS_ORIGINS: 'http://127.0.0.1:4300', }); function createMockPool(missingTable = ''): Pool { return { async query(text: string) { if (text.includes('to_regclass')) { const readiness: Record = { workspace: 'voc.workspace', product: 'voc.product', review: 'voc.review', sync_job: 'voc.sync_job', workspace_member: 'voc.workspace_member', analysis_run: 'voc.analysis_run', action_item: 'voc.action_item', alert: 'voc.alert', audit_log: 'voc.audit_log', }; if (missingTable) readiness[missingTable] = null; return { rows: [readiness], rowCount: 1, }; } if (text.includes('WITH target_workspace')) { return { rows: [{ public_id: '11111111-1111-4111-8111-111111111111', workspace_public_id: 'demashi', platform: 'jd', status: 'pending', scopes: ['product', 'reviews'], product_ids: ['11266507445'], progress: 0, attempts: 0, max_attempts: 3, error_summary: null, requested_at: '2026-07-23T00:00:00.000Z', started_at: null, completed_at: null, }], rowCount: 1, }; } if (text.includes('FROM voc.workspace_member')) { return { rows: [{ id: '1', public_id: 'demashi', user_external_id: 'local-admin', email: 'local-admin@localhost', display_name: 'Local Admin', role: 'owner', status: 'active', created_at: '2026-07-23T00:00:00.000Z', updated_at: '2026-07-23T00:00:00.000Z', }], rowCount: 1, }; } if (text.includes('FROM voc.workspace')) { return { rows: [{ id: '1', public_id: 'demashi', case_name: 'Demashi' }], rowCount: 1 }; } return { rows: [], rowCount: 0 }; }, } as unknown as Pool; } test('HTTP surface exposes health, empty snapshot, and sync queue contract', async () => { const app = createApp({ config, pool: createMockPool() }); const server = await new Promise>((resolve) => { const listening = app.listen(0, '127.0.0.1', () => resolve(listening)); }); try { const address = server.address() as AddressInfo; const baseUrl = `http://127.0.0.1:${address.port}`; const health = await fetch(`${baseUrl}/health`); assert.equal(health.status, 200); assert.equal((await health.json() as { database: string }).database, 'ready'); const snapshot = await fetch(`${baseUrl}/api/domestic-voc/snapshot?workspaceId=demashi&platform=jd`); assert.equal(snapshot.status, 200); assert.deepEqual((await snapshot.json() as { products: unknown[] }).products, []); const sync = await fetch(`${baseUrl}/api/domestic-voc/sync`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ productIds: ['11266507445'] }), }); assert.equal(sync.status, 202); assert.equal((await sync.json() as { job: { status: string } }).job.status, 'pending'); } finally { await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve())); } }); test('health reports a required platform migration instead of false readiness', async () => { const app = createApp({ config, pool: createMockPool('action_item') }); const server = await new Promise>((resolve) => { const listening = app.listen(0, '127.0.0.1', () => resolve(listening)); }); try { const address = server.address() as AddressInfo; const response = await fetch(`http://127.0.0.1:${address.port}/health`); assert.equal(response.status, 503); const body = await response.json() as { database: string; missingTables: string[] }; assert.equal(body.database, 'migration_required'); assert.deepEqual(body.missingTables, ['voc.action_item']); } finally { await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve())); } });