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.3 field types start at the frozen Yacimiento instead of the Area', () => { const structure = source('src/inspection-visits/f3-field-inventory-structure.service.ts'); assert.match(structure, /parentId \?\? base\.parent\.id/); assert.doesNotMatch(structure, /parentId \?\? base\.context\.area\.id/); }); test('F6.3 an Acta can start empty and receive Inventory when Hallazgos are added', () => { const dto = source('src/inspection-acts/dto/create-inspection-act.dto.ts'); const service = source('src/inspection-acts/inspection-acts.service.ts'); const mobile = source('../android-app/app/src/main/java/com/korexlabs/dhinspeccion/data/MobileActs.kt'); assert.doesNotMatch(dto, /ArrayMinSize\(1\)/); assert.match(service, /if \(assetIds\.length === 0\) return/); assert.match(mobile, /assetIds = emptyList\(\)/); }); test('F6.3 every Installation and Subinstallation receives the common field card', () => { const migration = source('src/database/migrations/1790099100000-f6-3-mobile-field-common-attributes.ts'); for (const label of ['Marca', 'Modelo', 'Capacidad', 'Número de serie', 'Función']) { assert.match(migration, new RegExp(label)); } assert.match(migration, /'instalacion','subinstalacion'/); assert.match(migration, /'TEXT'::asset_attribute_data_type/); assert.match(migration, /false, true/); }); test('F6.3 Android follows Inspección → Acta → Hallazgo → Inventario', () => { const root = source('../android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/ModernVisitRoot.kt'); const acts = source('../android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/ModernMobileActsScreen.kt'); const vm = source('../android-app/app/src/main/java/com/korexlabs/dhinspeccion/MainViewModel.kt'); assert.match(root, /model\.startVisit\(\); onActs\(\)/); assert.doesNotMatch(root, /Text\("Inventario de campo"/); assert.match(acts, /Text\("Agregar Hallazgo"\)/); assert.match(acts, /model\.createAct\(\)/); assert.match(acts, /model\.prepareSelectedAct\(closingUrgency\)/); assert.match(vm, /fun createAct\(\)/); assert.match(vm, /fun prepareSelectedAct\(urgency: String\)/); assert.match(vm, /repository\.fieldInventory\(currentVisit\.id, search, parentId\)/); }); test('F6.1 Acta urgency is null while drafting and is persisted atomically at lock', () => { const createDto = source('src/inspection-acts/dto/create-inspection-act.dto.ts'); const prepareDto = source('src/inspection-closing/dto/prepare-inspection-act.dto.ts'); const actsService = source('src/inspection-acts/inspection-acts.service.ts'); const closingService = source('src/inspection-closing/inspection-closing.service.ts'); const migration = source('src/database/migrations/1790113800000-f6-1-act-urgency-at-lock.ts'); assert.doesNotMatch(createDto, /urgency/); assert.match(prepareDto, /urgency!: InspectionActUrgency/); assert.match(actsService, /urgency: null/); assert.match(closingService, /urgency=\$2/); assert.match(migration, /ALTER COLUMN urgency DROP NOT NULL/); assert.match(migration, /SET urgency=NULL WHERE status='DRAFT'/); }); test('F6.3 Acta version capture uses a SELECT-shaped CTE so TypeORM never turns the version into NaN', () => { const service = source('src/inspection-acts/inspection-acts.service.ts'); assert.match(service, /WITH updated AS \(/); assert.match(service, /RETURNING current_version/); assert.match(service, /SELECT current_version AS "versionNumber" FROM updated/); assert.match(service, /const versionNumber = Number\(row\.versionNumber\)/); });