From 6e0adb7a07b653dec6a682425cac4dcf4e2d547b Mon Sep 17 00:00:00 2001 From: enlineawork Date: Fri, 11 Sep 2026 01:16:28 -0300 Subject: [PATCH] test: align operational context with Yacimiento ownership --- ...-1-operational-context-consistency.test.ts | 67 ++++++++----------- 1 file changed, 29 insertions(+), 38 deletions(-) diff --git a/api-v3/test/unit/f6-1-operational-context-consistency.test.ts b/api-v3/test/unit/f6-1-operational-context-consistency.test.ts index 250edc0..2dff3fc 100644 --- a/api-v3/test/unit/f6-1-operational-context-consistency.test.ts +++ b/api-v3/test/unit/f6-1-operational-context-consistency.test.ts @@ -7,24 +7,24 @@ function source(path: string): string { return readFileSync(resolve(process.cwd(), path), 'utf8'); } -test('F6.1 preserves Inventory operator snapshots and changes operator only through Area relations', () => { - const relations = source('src/asset-master/asset-operational-relations.service.ts'); - const migration = source('src/database/migrations/1790087250000-f5-operational-context-compatibility.ts'); +test('Authoritative model stores Company on Yacimiento and propagates it to physical descendants', () => { + const migration = source('src/database/migrations/1790106600000-authoritative-inventory-relationships.ts'); + const structure = source('src/asset-master/inventory-structure.service.ts'); - assert.match(migration, /Runtime ownership and search MUST NOT depend on it/); - assert.match(migration, /La Empresa se cambia en la relación temporal del Área, no en el Inventario/); - assert.match(relations, /operatorSnapshotPreserved:true/); - assert.doesNotMatch(relations, /UPDATE assets asset[\s\S]*SET operator_company_id=/); + assert.match(migration, /Yacimiento requiere Empresa relacionada/); + assert.match(migration, /NEW\.operational_area_id:=NEW\.parent_id/); + assert.match(migration, /NEW\.operator_company_id:=parent_company/); + assert.match(structure, /dto\.kind === 'YACIMIENTO'/); + assert.match(structure, /parent\?\.operatorCompanyId/); + assert.match(structure, /ensureCompatibilityProjection/); }); -test('F6.1 blocks operator mutation through generic Inventory edit DTOs', () => { +test('Authoritative Yacimiento relationships cannot be mutated through generic Inventory edits', () => { const updateDto = source('src/asset-master/dto/update-asset.dto.ts'); - const contextDto = source('src/asset-master/dto/change-asset-context.dto.ts'); - for (const dto of [updateDto, contextDto]) { - assert.match(dto, /@IsEmpty/); - assert.match(dto, /La Operadora se administra en la relación temporal del Área, no en el Inventario/); - } + assert.match(updateDto, /@IsEmpty/); + assert.match(updateDto, /La Empresa relacionada se administra en el Yacimiento/); + assert.match(updateDto, /El Tipo de concesión se administra en el Yacimiento/); }); test('F6.1 rejects legacy Inventory searches that filter by operator snapshot', () => { @@ -34,28 +34,25 @@ test('F6.1 rejects legacy Inventory searches that filter by operator snapshot', for (const dto of [listDto, treeDto]) { assert.match(dto, /operatorCompanyId\?: string/); assert.match(dto, /@IsEmpty/); - assert.match(dto, /Filtrá la Operadora mediante la relación temporal del Área/); } }); -test('F6.1 resolves temporal Inventory operator from Area relation history', () => { +test('Compatibility Area relation history remains available for legacy temporal queries', () => { const temporal = source('src/asset-master/asset-temporal.service.ts'); + const migration = source('src/database/migrations/1790106600000-authoritative-inventory-relationships.ts'); assert.match(temporal, /FROM area_company_relations relation/); - assert.match(temporal, /relation\.valid_from <= \$2/); - assert.match(temporal, /relation\.valid_until IS NULL OR relation\.valid_until > \$2/); - assert.match(temporal, /operator_company\.id=operator_relation\.company_id/); - assert.doesNotMatch(temporal, /operator_company\.id=history\.operator_company_id/); + assert.match(migration, /Derivado automáticamente de Yacimientos del modelo autoritativo/); + assert.match(migration, /TRUNCATE TABLE area_company_relations CASCADE/); }); -test('F6.1 verification planning uses Area relation truth instead of Inventory operator snapshot', () => { +test('F6.1 verification planning can consume the Area compatibility projection', () => { const verification = source('src/inspection-verifications/inspection-verifications.service.ts'); + const migration = source('src/database/migrations/1790106600000-authoritative-inventory-relationships.ts'); assert.match(verification, /FROM area_company_relations relation/); - assert.match(verification, /relation\.valid_from <= \$2::timestamptz/); assert.match(verification, /VERIFICATION_OPERATOR_NOT_ACTIVE/); - assert.match(verification, /operatorSource: 'area_company_relations'/); - assert.doesNotMatch(verification, /company\.id = asset\.operator_company_id/); + assert.match(migration, /relation_role,source_document_id,valid_from,start_reason/); }); test('F6.1 historical operational lists read Company and Area from the parent Inspection', () => { @@ -68,11 +65,9 @@ test('F6.1 historical operational lists read Company and Area from the parent In assert.doesNotMatch(acts, /context_asset\.operator_company_id/); assert.match(findings, /company\.id = visit\.operator_company_id/); assert.match(findings, /area\.id = visit\.operational_area_id/); - assert.doesNotMatch(findings, /company\.id = asset\.operator_company_id/); assert.match(reports, /visit\.\$\{column\} = \$\{parameter\}::uuid/); assert.match(reports, /company\.id=context_visit\.operator_company_id/); assert.match(reports, /area\.id=context_visit\.operational_area_id/); - assert.doesNotMatch(reports, /context_asset\.operator_company_id/); }); test('F6.1 WEB Inventory searches use physical scope and never the operator snapshot filter', () => { @@ -85,26 +80,22 @@ test('F6.1 WEB Inventory searches use physical scope and never the operator snap assert.match(inspectionEditor, /listInspectionPreventiveCandidates\(id, assetSearch\)/); assert.match(preventiveApi, /inspection-visits\/\$\{visitId\}\/preventive-candidates/); assert.match(preventiveService, /COALESCE\(visit\.scope_asset_id, visit\.operational_area_id\) AS "scopeAssetId"/); - assert.doesNotMatch(preventiveService, /asset\.operator_company_id/); assert.doesNotMatch(inspectionEditor, /listAssets\(\{[\s\S]{0,300}operatorCompanyId:/); for (const page of [fieldDiscoveries, inventoryMerge]) { assert.match(page, /listAssets\(\{[\s\S]{0,300}operationalAreaId:/); assert.doesNotMatch(page, /listAssets\(\{[\s\S]{0,300}operatorCompanyId:/); } - assert.doesNotMatch(inventoryMerge, /source\.operatorCompany/); }); -test('F6.1 WEB derives the creation snapshot from the active Area operator and removes it from Inventory edits', () => { - const editor = source('../web-v2/src/pages/AssetEditorPage.tsx'); - const contextPanel = source('../web-v2/src/features/assets/AssetContextHistoryPanel.tsx'); +test('WEB creation captures Company and concession only while creating a Yacimiento', () => { + const page = source('../web-v2/src/pages/InventoryCreatePage.tsx'); + const structure = source('src/asset-master/inventory-structure.service.ts'); - assert.match(editor, /createAsset\(\{[\s\S]*operatorCompanyId: operatorCompanyId \|\| null/); - assert.doesNotMatch(editor, /updateAsset\(id, \{[^}]*operatorCompanyId/); - assert.doesNotMatch(editor, /parent\.operatorCompany/); - assert.match(editor, /listCompaniesForArea\(operationalAreaId\)\.then\(\(loadedCompanies\)/); - assert.match(editor, /loadedCompanies\.length === 1 \? \(loadedCompanies\[0\]\?\.id \?\? ''\) : ''/); - assert.match(editor, /Snapshot histórico de alta/); - assert.match(contextPanel, /Cambiar contexto físico/); - assert.doesNotMatch(contextPanel, /operatorCompanyId:/); + assert.match(page, /requiresYacimientoContext = kind === 'YACIMIENTO'/); + assert.match(page, /Empresa relacionada/); + assert.match(page, /Tipo de concesión/); + assert.match(page, /operatorCompanyId:operatorCompanyId \|\| null/); + assert.match(page, /concessionTypeId:concessionTypeId \|\| null/); + assert.match(structure, /Empresa, Departamento, Área y Yacimiento no llevan clasificación técnica/); });