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/1788717600000-phase-d5-3-23-field-discoveries.ts'); const assets = read('src/asset-master/assets.service.ts'); const inspectionLinks = read('src/inspection-operations/field-discovery-inspection-link.service.ts'); const controller = read('src/asset-master/assets.controller.ts'); const mobilePolicy = read('src/inspection-operations/mobile-inspector-policy.ts'); const findings = read('src/inspection-findings/inspection-findings.service.ts'); test('D5.3.23 records field discoveries as auditable provisional inventory records', () => { assert.match(migration, /asset_field_discoveries/); assert.match(migration, /status IN \('PENDING','APPROVED','MATCHED','REJECTED'\)/); assert.match(assets, /AssetDataOrigin\.FIELD_SURVEY/); assert.match(assets, /AssetInformationStatus\.DRAFT/); assert.match(assets, /inspection-visit:/); }); test('D5.3.23 only lets a mobile assigned inspector create during an active visit', () => { assert.match(assets, /assertMobileInspector\(principal\)/); assert.match(mobilePolicy, /principal\.transport !== 'bearer'/); assert.match(assets, /visit\.status !== 'IN_PROGRESS'/); assert.match(assets, /FIELD_DISCOVERY_INSPECTOR_NOT_ASSIGNED/); }); test('F1.1 preserves D5.3.23 field discovery while linking only to the current draft act', () => { assert.match(assets, /fieldDiscoveryInspectionLinks\.attach/); assert.doesNotMatch(assets, /INSERT INTO inspection_/); assert.match(inspectionLinks, /INSERT INTO inspection_visit_assets/); assert.match(inspectionLinks, /INSERT INTO inspection_act_assets/); assert.match(inspectionLinks, /status = 'DRAFT'/); assert.match(inspectionLinks, /if \(act\)/); assert.match(inspectionLinks, /actId: act\?\.id \?\? null/); assert.match(findings, /assertActAsset/); }); test('D5.3.23 office review can approve match or reject without deleting historical objects', () => { assert.match(controller, /field-discoveries\/:id\/approve/); assert.match(controller, /field-discoveries\/:id\/match/); assert.match(controller, /field-discoveries\/:id\/reject/); assert.match(controller, /@RequirePermissions\('assets\.change_status'\)/); assert.match(assets, /AssetInformationStatus\.VALIDATED/); assert.match(assets, /ASSET_TYPE_CHANGE_NOT_ALLOWED/); assert.match(assets, /AssetInformationStatus\.INACTIVE/); assert.doesNotMatch(assets, /DELETE FROM assets/); }); test('D5.3.23 preserves immutable inspection history when matching an existing inventory object', () => { assert.match(migration, /matched_asset_id uuid/); assert.match(migration, /matched_asset_id <> asset_id/); assert.match(assets, /FIELD_DISCOVERY_MATCH_CONTEXT_MISMATCH/); assert.doesNotMatch(assets, /UPDATE inspection_findings SET asset_id/); assert.doesNotMatch(assets, /UPDATE inspection_act_assets SET asset_id/); });