import 'reflect-metadata'; import assert from 'node:assert/strict'; import test from 'node:test'; import { readFileSync } from 'node:fs'; const migration = readFileSync( 'src/database/migrations/1789844600000-phase-f3-1-asset-chronological-merge.ts', 'utf8', ); const service = readFileSync('src/asset-master/inventory-merge.service.ts', 'utf8'); const controller = readFileSync('src/asset-master/inventory-merge.controller.ts', 'utf8'); test('F3.1 registra cada fusión como evento append-only con snapshots', () => { assert.match(migration, /CREATE TABLE asset_merges/); assert.match(migration, /source_snapshot jsonb NOT NULL/); assert.match(migration, /canonical_snapshot jsonb NOT NULL/); assert.match(migration, /UNIQUE \(source_asset_id\)/); assert.match(migration, /BEFORE UPDATE OR DELETE ON asset_merges/); assert.match(migration, /append-only/); }); test('F3.1/F5 sólo permite fusionar Instalaciones o Subinstalaciones compatibles dentro de la misma Área física', () => { assert.match(service, /MERGEABLE_TYPES = new Set\(\['instalacion', 'subinstalacion'\]\)/); assert.match(service, /INVENTORY_MERGE_TYPE_INVALID/); assert.match(service, /resolvePhysicalAreaId/); assert.match(service, /INVENTORY_MERGE_AREA_MISMATCH/); assert.match(service, /INVENTORY_MERGE_PARENT_MISMATCH/); assert.match(service, /INVENTORY_MERGE_CHILD_FAMILY_CONFLICT/); assert.doesNotMatch(service, /source\.operatorCompanyId\s*!==\s*canonical\.operatorCompanyId/); }); test('F3.1 preserva referencias históricas y sólo reubica hijos actuales', () => { assert.match(service, /historyPolicy: 'HISTORICAL_REFERENCES_PRESERVED'/); assert.match(service, /historicalReferencesRewritten: false/); assert.doesNotMatch(service, /UPDATE\s+inspection_findings/i); assert.doesNotMatch(service, /UPDATE\s+inspection_act_assets/i); assert.doesNotMatch(service, /UPDATE\s+inspection_visit_assets/i); assert.match(service, /Reparentado por fusión cronológica/); assert.match(service, /AssetVersionChangeType\.UPDATED/); }); test('F3.1 expone estado/merge en WEB y conciliación de alta nacida en la inspección para APK', () => { assert.match(controller, /:id\/merge-status/); assert.match(controller, /:id\/merge/); assert.match(controller, /:assetId\/merge/); assert.match(controller, /assets\.create/); assert.match(controller, /inspections\.execute/); assert.match(service, /FIELD_MERGE_SOURCE_MUST_BE_DISCOVERY/); assert.match(service, /assertMobileInspector\(principal\)/); }); test('F3.1 resuelve cadenas de merge hacia un canónico final y detecta ciclos', () => { assert.match(service, /WITH RECURSIVE chain/); assert.match(service, /INVENTORY_MERGE_CYCLE/); assert.match(service, /aliasesFor/); });