/** * lib/services/route53-write-persistence.ts unit tests — SQL contract checks * against a mocked postgresClient. No real Postgres connection is made. * Follows the mocking discipline in lib/services/pax8-sync-service.test.ts. */ import { describe, it, expect, vi, beforeEach } from 'vitest'; // Mock postgresClient BEFORE importing the module under test. const queryMock = vi.fn(); vi.mock('@/lib/services/postgres-client', () => ({ default: { query: (...args: unknown[]) => queryMock(...args), }, })); // Import AFTER the mock is declared so vi.mock hoisting takes effect. import { createPendingAuditLog, markAuditCommitted, markAuditFailed, insertPulseCrudHistory, upsertMirrorRecord, softDeleteMirrorRecord, loadMirrorRecord, } from './route53-write-persistence'; interface MockCall { sql: string; params: unknown[]; } function calls(): MockCall[] { return queryMock.mock.calls.map(([sql, params]) => ({ sql: String(sql), params: (params as unknown[]) ?? [], })); } beforeEach(() => { queryMock.mockReset(); }); describe('createPendingAuditLog', () => { it("issues an INSERT into route53_audit_log whose SQL contains 'pending'", async () => { queryMock.mockResolvedValueOnce({ rows: [{ id: 'audit-1' }] }); const result = await createPendingAuditLog({ operation: 'create', zoneId: 'Z123', recordKey: 'Z123:www.example.com.:A:', recordName: 'www.example.com.', recordType: 'A', beforeValue: null, afterValue: { ttl: 300, resourceRecords: [{ value: '1.2.3.4' }] }, performedByUserId: 'user-1', performedByEmail: 'lorentz@wulfconsulting.com', }); expect(result).toEqual({ id: 'audit-1' }); expect(calls()).toHaveLength(1); expect(calls()[0].sql).toContain('INSERT INTO route53_audit_log'); expect(calls()[0].sql).toContain("'pending'"); }); }); describe('markAuditCommitted', () => { it("issues an UPDATE setting status = 'committed'", async () => { queryMock.mockResolvedValueOnce({ rows: [] }); await markAuditCommitted('audit-1', 'change-1', 'INSYNC', { ResponseMetadata: {} }); expect(calls()[0].sql).toContain('UPDATE route53_audit_log'); expect(calls()[0].sql).toContain("'committed'"); }); }); describe('markAuditFailed', () => { it('passes a sanitized error string — a raw AWS key id never reaches the bound parameters', async () => { queryMock.mockResolvedValueOnce({ rows: [] }); const rawError = new Error('AccessDenied for AKIAIOSFODNN7EXAMPLE'); await markAuditFailed('audit-1', rawError); expect(calls()).toHaveLength(1); expect(calls()[0].sql).toContain("'failed'"); const boundParams = calls()[0].params; expect(boundParams).not.toContain(rawError); for (const param of boundParams) { if (typeof param === 'string') { expect(param).not.toContain('AKIAIOSFODNN7EXAMPLE'); } } }); }); describe('insertPulseCrudHistory', () => { it("binds the literal 'pulse_crud' as the history source", async () => { queryMock.mockResolvedValueOnce({ rows: [] }); await insertPulseCrudHistory({ zoneId: 'Z123', recordKey: 'Z123:www.example.com.:A:', recordName: 'www.example.com.', recordType: 'A', changeAction: 'create', beforeValue: null, afterValue: { ttl: 300 }, changedByUserId: 'user-1', changedByEmail: 'lorentz@wulfconsulting.com', auditLogId: 'audit-1', }); expect(calls()[0].sql).toContain('INSERT INTO route53_record_history'); expect(calls()[0].sql).toContain("'pulse_crud'"); }); }); describe('upsertMirrorRecord', () => { it('issues an INSERT ... ON CONFLICT upsert and never throws even if the query rejects', async () => { queryMock.mockRejectedValueOnce(new Error('connection reset')); await expect( upsertMirrorRecord({ recordKey: 'Z123:www.example.com.:A:', zoneId: 'Z123', name: 'www.example.com.', type: 'A', setIdentifier: null, ttl: 300, resourceRecords: [{ value: '1.2.3.4' }], aliasTarget: null, rawPayload: null, }) ).resolves.toBeUndefined(); }); }); describe('softDeleteMirrorRecord', () => { it('issues an UPDATE and never a DELETE FROM', async () => { queryMock.mockResolvedValueOnce({ rows: [] }); await softDeleteMirrorRecord('Z123:www.example.com.:A:'); expect(calls()[0].sql).toContain('UPDATE route53_records'); expect(calls()[0].sql).not.toContain('DELETE FROM'); }); it('never throws even if the query rejects (best-effort)', async () => { queryMock.mockRejectedValueOnce(new Error('connection reset')); await expect(softDeleteMirrorRecord('Z123:www.example.com.:A:')).resolves.toBeUndefined(); }); }); describe('loadMirrorRecord', () => { it('returns a camelCase object when found', async () => { queryMock.mockResolvedValueOnce({ rowCount: 1, rows: [ { record_key: 'Z123:www.example.com.:A:', zone_id: 'Z123', name: 'www.example.com.', type: 'A', set_identifier: null, ttl: 300, resource_records: [{ value: '1.2.3.4' }], alias_target: null, }, ], }); const result = await loadMirrorRecord('Z123:www.example.com.:A:'); expect(result).toEqual({ recordKey: 'Z123:www.example.com.:A:', zoneId: 'Z123', name: 'www.example.com.', type: 'A', setIdentifier: null, ttl: 300, resourceRecords: [{ value: '1.2.3.4' }], aliasTarget: null, }); }); it('returns null when not found', async () => { queryMock.mockResolvedValueOnce({ rowCount: 0, rows: [] }); const result = await loadMirrorRecord('missing-key'); expect(result).toBeNull(); }); });