/** * @ai-summary Integration tests for audit_logs table migration * @ai-context Tests table creation, constraints, and indexes */ import { Pool } from 'pg'; import { appConfig } from '../../../core/config/config-loader'; describe('Audit Logs Migration', () => { let pool: Pool; beforeAll(async () => { pool = new Pool({ connectionString: appConfig.getDatabaseUrl(), }); }); afterAll(async () => { await pool.end(); }); describe('Table Structure', () => { it('should have audit_logs table with correct columns', async () => { const result = await pool.query(` SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_name = 'audit_logs' ORDER BY ordinal_position `); const columns = result.rows.map((row) => row.column_name); expect(columns).toContain('id'); expect(columns).toContain('category'); expect(columns).toContain('severity'); expect(columns).toContain('user_id'); expect(columns).toContain('action'); expect(columns).toContain('resource_type'); expect(columns).toContain('resource_id'); expect(columns).toContain('details'); expect(columns).toContain('created_at'); }); }); describe('CHECK Constraints', () => { it('should accept valid category values', async () => { const validCategories = ['auth', 'vehicle', 'user', 'system', 'admin']; for (const category of validCategories) { const result = await pool.query( `INSERT INTO audit_logs (category, severity, action) VALUES ($1, 'info', 'test action') RETURNING id`, [category] ); expect(result.rows[0].id).toBeDefined(); // Cleanup await pool.query('DELETE FROM audit_logs WHERE id = $1', [result.rows[0].id]); } }); it('should reject invalid category values', async () => { await expect( pool.query( `INSERT INTO audit_logs (category, severity, action) VALUES ('invalid', 'info', 'test action')` ) ).rejects.toThrow(); }); it('should accept valid severity values', async () => { const validSeverities = ['info', 'warning', 'error']; for (const severity of validSeverities) { const result = await pool.query( `INSERT INTO audit_logs (category, severity, action) VALUES ('auth', $1, 'test action') RETURNING id`, [severity] ); expect(result.rows[0].id).toBeDefined(); // Cleanup await pool.query('DELETE FROM audit_logs WHERE id = $1', [result.rows[0].id]); } }); it('should reject invalid severity values', async () => { await expect( pool.query( `INSERT INTO audit_logs (category, severity, action) VALUES ('auth', 'invalid', 'test action')` ) ).rejects.toThrow(); }); }); describe('Nullable Columns', () => { it('should allow NULL user_id for system actions', async () => { const result = await pool.query( `INSERT INTO audit_logs (category, severity, user_id, action) VALUES ('system', 'info', NULL, 'system startup') RETURNING id, user_id` ); expect(result.rows[0].id).toBeDefined(); expect(result.rows[0].user_id).toBeNull(); // Cleanup await pool.query('DELETE FROM audit_logs WHERE id = $1', [result.rows[0].id]); }); }); describe('Indexes', () => { it('should have required indexes', async () => { const result = await pool.query(` SELECT indexname FROM pg_indexes WHERE tablename = 'audit_logs' `); const indexNames = result.rows.map((row) => row.indexname); expect(indexNames).toContain('idx_audit_logs_category_created'); expect(indexNames).toContain('idx_audit_logs_severity_created'); expect(indexNames).toContain('idx_audit_logs_user_created'); expect(indexNames).toContain('idx_audit_logs_created'); expect(indexNames).toContain('idx_audit_logs_action_gin'); }); }); });