import assert from 'node:assert/strict'; import test from 'node:test'; import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; const read = (path: string) => readFileSync(resolve(process.cwd(), path), 'utf8'); const migration = read('src/database/migrations/1788890400000-phase-d5-3-25-verification-event-history.ts'); const findings = read('src/inspection-findings/inspection-findings.service.ts'); const findingsController = read('src/inspection-findings/inspection-findings.controller.ts'); const verifications = read('src/inspection-verifications/inspection-verifications.service.ts'); const ledger = read('src/inspection-verifications/verification-event-ledger.ts'); test('D5.3.25 creates an append-only verification event ledger and reconstructs prior history', () => { assert.match(migration, /CREATE TABLE inspection_finding_verification_events/); assert.match(migration, /FROM inspection_finding_versions version/); assert.match(migration, /FROM inspection_finding_verification_visits verification_link/); assert.match(migration, /REVOKE UPDATE, DELETE ON TABLE inspection_finding_verification_events/); assert.doesNotMatch(ledger, /UPDATE inspection_finding_verification_events/); assert.doesNotMatch(ledger, /DELETE FROM inspection_finding_verification_events/); }); test('D5.3.25 records every office control-date change without replacing historical events', () => { assert.match(findings, /dto\.nextControlOn !== undefined && dto\.nextControlOn !== before\.nextControlOn/); assert.match(findings, /InspectionFindingVerificationEventType\.CONTROL_DATE_DEFINED/); assert.match(findings, /InspectionFindingVerificationEventType\.CONTROL_DATE_CHANGED/); assert.match(findings, /InspectionFindingVerificationEventType\.CONTROL_DATE_CLEARED/); assert.match(findings, /appendVerificationEvent\(manager/); }); test('D5.3.25 makes one field result immutable and appends its result event', () => { assert.match(verifications, /VERIFICATION_RESULT_IMMUTABLE/); assert.match(verifications, /InspectionFindingVerificationEventType\.RESULT_RECORDED/); assert.match(migration, /protect_inspection_verification_result_immutability/); assert.match(migration, /trg_inspection_verification_result_immutable/); assert.match(migration, /OLD\.result_recorded_at IS NOT NULL/); }); test('D5.3.25 preserves successive verification visits as separate historical attempts', () => { assert.match(verifications, /visit\.status IN \('DRAFT', 'PLANNED', 'IN_PROGRESS'\)/); assert.match(verifications, /INSERT INTO inspection_finding_verification_visits/); assert.match(verifications, /InspectionFindingVerificationEventType\.VISIT_PLANNED/); assert.doesNotMatch(verifications, /DELETE FROM inspection_finding_verification_visits/); }); test('D5.3.25 exposes the complete verification history through finding detail and a dedicated endpoint', () => { assert.match(findingsController, /@Get\(':id\/verification-history'\)/); assert.match(findings, /loadVerificationHistory/); assert.match(findings, /inspection_finding_verification_events event/); assert.match(findings, /verificationHistory/); assert.match(findings, /evidenceCount/); });