// Migration Idempotency Integration Test // This test requires a running PostgreSQL instance with DATABASE_URL configured in backend/.env. // It runs ALL Postgres migrations twice (via run-all.js) to verify they are idempotent (safe to re-run), // then checks that key tables and columns exist. // // Run separately: npx jest backend/__tests__/migrations-idempotency.integration.test.js --forceExit const { execSync } = require('child_process'); const path = require('path'); // The real pool — NOT mocked. This hits the actual database. const pool = require('../db'); const BACKEND_DIR = path.join(__dirname, '..'); function runAllMigrations() { execSync('node migrations/run-all.js', { cwd: BACKEND_DIR, stdio: 'pipe', timeout: 30000, }); } afterAll(async () => { await pool.end(); }); describe('Migration Idempotency', () => { it('runs all migrations twice without errors (idempotent)', () => { // First run runAllMigrations(); // Second run — should not throw if migrations are truly idempotent runAllMigrations(); }, 30000); it('key tables exist after migrations', async () => { const expectedTables = [ 'compliance_items', 'compliance_item_history', 'compliance_notes', 'jira_tickets', 'ivanti_fp_submissions', ]; const { rows } = await pool.query(` SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_name = ANY($1) `, [expectedTables]); const foundTables = rows.map(r => r.table_name); for (const table of expectedTables) { expect(foundTables).toContain(table); } }, 30000); it('compliance_item_history has expected columns', async () => { const expectedColumns = [ 'id', 'hostname', 'field_name', 'old_value', 'new_value', 'change_reason', 'changed_by', 'changed_at', 'metric_id', ]; const { rows } = await pool.query(` SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'compliance_item_history' `); const foundColumns = rows.map(r => r.column_name); for (const col of expectedColumns) { expect(foundColumns).toContain(col); } }, 30000); });