From ab5ce42d47379bddf30e0dcb6a5e20d0d7fcf09e Mon Sep 17 00:00:00 2001 From: enlineawork Date: Wed, 9 Sep 2026 10:48:50 -0300 Subject: [PATCH] test(f6): lock definitive inventory and mobile contracts --- .../unit/f6-solid-inventory-contract.test.ts | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 api-v3/test/unit/f6-solid-inventory-contract.test.ts diff --git a/api-v3/test/unit/f6-solid-inventory-contract.test.ts b/api-v3/test/unit/f6-solid-inventory-contract.test.ts new file mode 100644 index 0000000..8888132 --- /dev/null +++ b/api-v3/test/unit/f6-solid-inventory-contract.test.ts @@ -0,0 +1,90 @@ +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import test from 'node:test'; + +function source(path: string) { + return readFileSync(resolve(process.cwd(), path), 'utf8'); +} + +test('F6 models concrete Installation 1→N Subinstallations while each child keeps exactly one physical parent', () => { + const asset = source('src/database/entities/asset.entity.ts'); + const migration = source('src/database/migrations/1790091000000-f6-solid-inventory-model.ts'); + + assert.match(asset, /@Column\(\{ name: 'parent_id', type: 'uuid', nullable: true \}\)/); + assert.match(asset, /parentId!: string \| null/); + assert.match(migration, /IF child_type='subinstalacion' THEN/); + assert.match(migration, /IF NEW\.parent_id IS NULL THEN/); + assert.doesNotMatch(asset, /parentIds!/); +}); + +test('F6 makes technical Installation↔Subinstallation compatibility truly many-to-many', () => { + const migration = source('src/database/migrations/1790091000000-f6-solid-inventory-model.ts'); + const catalog = source('src/asset-master/inventory-family-catalog.service.ts'); + + assert.match(migration, /PRIMARY KEY \(child_family_id,parent_family_id\)/); + assert.match(catalog, /parentFamilyIds: string\[\]/); + assert.match(catalog, /requestedIds:string\[\]/); + assert.match(catalog, /INSERT INTO inventory_family_parent_rules\(child_family_id,parent_family_id\)/); + assert.match(catalog, /UNNEST\(\$2::uuid\[\]\)/); + assert.match(catalog, /ON CONFLICT \(child_family_id,parent_family_id\) DO NOTHING/); +}); + +test('F6 database rejects a Subinstallation classification incompatible with its concrete Installation parent', () => { + const migration = source('src/database/migrations/1790091000000-f6-solid-inventory-model.ts'); + + assert.match(migration, /SELECT inventory_family_id INTO parent_family FROM assets WHERE id=NEW\.parent_id/); + assert.match(migration, /rule\.child_family_id=NEW\.inventory_family_id AND rule\.parent_family_id=parent_family/); + assert.match(migration, /La clasificación de Subinstalación no es compatible con la Instalación padre/); +}); + +test('F6 technical values are defined by classification and cannot leak between families', () => { + const migration = source('src/database/migrations/1790091000000-f6-solid-inventory-model.ts'); + const service = source('src/asset-master/inventory-technical-values.service.ts'); + + assert.match(migration, /CREATE TABLE IF NOT EXISTS inventory_family_attribute_definitions/); + assert.match(migration, /CREATE TABLE IF NOT EXISTS asset_inventory_attribute_values/); + assert.match(migration, /El campo técnico no pertenece a la clasificación del Inventario/); + assert.match(service, /inventory_family_attribute_definitions/); + assert.match(service, /asset_inventory_attribute_values/); +}); + +test('F6 field findings resolve by exact technical classification and keep OTROS available', () => { + const resolver = source('src/inspection-findings/f3-finding-catalog-resolver.service.ts'); + + assert.match(resolver, /mapping\.inventory_family_id=\$1::uuid/); + assert.match(resolver, /asset\.inventory_family_id AS "familyId"/); + assert.doesNotMatch(resolver, /category\.code='f5model'/); + assert.match(resolver, /code: 'OTHER'/); + assert.match(resolver, /label: 'OTROS'/); +}); + +test('F6 mobile finding gate uses Area ancestry and temporal operator relation instead of copying company ownership onto each object', () => { + const fieldFindings = source('src/inspection-visits/field-findings.service.ts'); + + assert.match(fieldFindings, /WITH RECURSIVE ancestors/); + assert.match(fieldFindings, /WHERE id=visit\.operational_area_id/); + assert.match(fieldFindings, /FROM area_company_relations relation/); + assert.match(fieldFindings, /relation\.relation_role='OPERATOR'/); + assert.doesNotMatch(fieldFindings, /asset\.operator_company_id=visit\.operator_company_id/); +}); + +test('F6 field-created technical Inventory may be provisional only until the selected family is assigned', () => { + const migration = source('src/database/migrations/1790091000000-f6-solid-inventory-model.ts'); + const fieldStructure = source('src/inspection-visits/f3-field-inventory-structure.service.ts'); + const fieldFindings = source('src/inspection-visits/field-findings.service.ts'); + + assert.match(migration, /NEW\.data_origin='FIELD_SURVEY' AND NEW\.information_status='DRAFT'/); + assert.match(fieldStructure, /SET inventory_family_id=\$2::uuid/); + assert.match(fieldFindings, /FIELD_FINDING_FAMILY_REQUIRED/); +}); + +test('F6 Android keeps search and reloads scoped to the current physical parent', () => { + const viewModel = source('../android-app/app/src/main/java/com/korexlabs/dhinspeccion/MainViewModel.kt'); + const screen = source('../android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/F3VisitRoot.kt'); + + assert.match(viewModel, /var inventoryParentId: String\? by mutableStateOf\(null\)/); + assert.match(viewModel, /effectiveParentId = parentId \?: inventoryParentId \?: currentVisit\.operationalArea\?\.id/); + assert.match(viewModel, /repository\.fieldInventory\(currentVisit\.id, null, effectiveParentId\)/); + assert.match(screen, /model\.loadFieldTypes\(item\.id\)/); +});