import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import test from 'node:test'; const root = process.cwd(); const read = (relative: string) => fs.readFileSync(path.join(root, relative), 'utf8'); test('D5.5 stores explicit Area and Operator context plus append-only checklist generations', () => { const migration = read('src/database/migrations/1789063200000-phase-d5-5-web-planning-checklist.ts'); assert.match(migration, /ADD COLUMN operational_area_id uuid/); assert.match(migration, /ADD COLUMN operator_company_id uuid/); assert.match(migration, /CREATE TABLE inspection_visit_checklist_items/); assert.match(migration, /generation_number integer NOT NULL/); assert.match(migration, /REVOKE UPDATE, DELETE ON TABLE[\s\S]*inspection_visit_checklist_items, inspection_visit_asset_events/); }); test('D5.5 exposes Area then active OPERATOR choices and a server-generated planning checklist', () => { const controller = read('src/inspection-visits/inspection-visits.controller.ts'); const service = read('src/inspection-visits/inspection-visits.service.ts'); assert.match(controller, /@Get\('planning-context\/areas'\)/); assert.match(controller, /@Get\('planning-context\/areas\/:areaId\/operators'\)/); assert.match(controller, /@Post\(':id\/checklist\/generate'\)/); assert.match(service, /relation\.relation_role = 'OPERATOR'/); assert.match(service, /INSPECTION_OPERATOR_NOT_ACTIVE_IN_AREA/); }); test('D5.5 classifies antecedents overdue items and next controls while adding actionable assets automatically', () => { const service = read('src/inspection-visits/inspection-visits.service.ts'); assert.match(service, /'COMPANY_OVERDUE'/); assert.match(service, /'VERIFICATION_OVERDUE'/); assert.match(service, /'UPCOMING_CONTROL'/); assert.match(service, /'ANTECEDENT'/); assert.match(service, /item_kind IN \('COMPANY_OVERDUE','VERIFICATION_OVERDUE','UPCOMING_CONTROL'\)/); assert.match(service, /'AUTO_INCLUDED'/); }); test('D5.5 treats manual additions as preventive and requires an auditable reason for exclusions', () => { const service = read('src/inspection-visits/inspection-visits.service.ts'); const dto = read('src/inspection-visits/dto/exclude-inspection-visit-asset.dto.ts'); assert.match(service, /planning_source[^\n]*[\s\S]*'PREVENTIVE'/); assert.match(service, /INSPECTION_EXCLUSION_REASON_REQUIRED/); assert.match(service, /INSPECTION_VISIT_ASSET_EXCLUDED/); assert.match(service, /INSPECTION_VISIT_ASSET_REINCLUDED/); assert.match(dto, /@MinLength\(10\)/); }); test('D5.5 keeps verification visits inside the same Area Operator planning contract and start remains APK-only', () => { const verification = read('src/inspection-verifications/inspection-verifications.service.ts'); const visits = read('src/inspection-visits/inspection-visits.service.ts'); assert.match(verification, /operationalAreaId: areaId/); assert.match(verification, /operatorCompanyId: companyId/); assert.match(verification, /planning_source = 'VERIFICATION'/); assert.match(visits, /assertMobileInspector\(principal\)/); assert.match(visits, /INSPECTION_CHECKLIST_REQUIRED/); }); test('D5.5 confirmation requires a reviewed current checklist and keeps visit start under mobile inspector policy', () => { const visits = read('src/inspection-visits/inspection-visits.service.ts'); const policy = read('src/inspection-operations/mobile-inspector-policy.ts'); assert.match(visits, /INSPECTION_CHECKLIST_REQUIRED/); assert.match(visits, /checklistGeneratedAt/); assert.match(visits, /assertMobileInspector\(principal\)/); assert.match(policy, /transport !== 'bearer'/); assert.match(policy, /includes\('inspector'\)/); });