import Database from 'better-sqlite3'; import { describe, expect, it } from 'vitest'; import { migrateDatabase } from './infrastructure/database/migrations.js'; import { createProductionReadiness } from './production-readiness.js'; function fixture() { const db = new Database(':memory:'); migrateDatabase(db); return db; } describe('production readiness release gates', () => { it('fails closed on pending credentials, recovery markers, missing assets and configured gateway failure', async () => { const db = fixture(); db.prepare( "INSERT INTO app_settings(key,value_json,created_at,updated_at) VALUES('legacyImport.pendingCredentials','2','x','x')", ).run(); const readiness = createProductionReadiness({ db, gatewayToken: 'x'.repeat(32), keychainMetadataCheck: async () => true, assetsCheck: async () => false, recoveryMarkersCheck: async () => 1, upstreamGatewayCheck: async () => false, }); await expect(readiness()).resolves.toEqual({ ready: false, checks: { database: 'ready', migrations: 'ready', gatewayAuth: 'ready', keychain: 'ready', pendingCredentials: 'pending:2', recoveryMarkers: 'present:1', assets: 'unavailable', upstreamGateway: 'unavailable', }, }); db.close(); }); it('requires explicit release checks and reports ready only when every gate passes', async () => { const db = fixture(); const missing = await createProductionReadiness({ db, gatewayToken: 'x'.repeat(32), keychainMetadataCheck: async () => true, })(); expect(missing.ready).toBe(false); expect(missing.checks).toMatchObject({ assets: 'unconfigured', recoveryMarkers: 'unconfigured', upstreamGateway: 'unconfigured', pendingCredentials: 'ready', }); const ready = await createProductionReadiness({ db, gatewayToken: 'x'.repeat(32), keychainMetadataCheck: async () => true, assetsCheck: async () => true, recoveryMarkersCheck: async () => 0, upstreamGatewayCheck: async () => true, })(); expect(ready.ready).toBe(true); db.close(); }); });