diff --git a/api-v3/test/unit/f3-1-chronological-merge.test.ts b/api-v3/test/unit/f3-1-chronological-merge.test.ts new file mode 100644 index 0000000..8f9b96c --- /dev/null +++ b/api-v3/test/unit/f3-1-chronological-merge.test.ts @@ -0,0 +1,54 @@ +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 sólo permite fusionar Instalaciones o Subinstalaciones compatibles', () => { + assert.match(service, /MERGEABLE_TYPES = new Set\(\['instalacion', 'subinstalacion'\]\)/); + assert.match(service, /INVENTORY_MERGE_TYPE_INVALID/); + assert.match(service, /INVENTORY_MERGE_CONTEXT_MISMATCH/); + assert.match(service, /INVENTORY_MERGE_PARENT_MISMATCH/); + assert.match(service, /INVENTORY_MERGE_CHILD_FAMILY_CONFLICT/); +}); + +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/); +});