Files
dh-inspeccion-v2/api-v3/test/unit/f6-8-offline-document-contract.test.ts
T
DH V2 079728aa6d
Android CI / RC / Android · lint, tests, debug APK, release compile (push) Successful in 3m41s
DH V2 CI / API · typecheck, tests, build (push) Successful in 31s
DH V2 CI / WEB · typecheck, build (push) Successful in 19s
Production dependency audit / API · production dependencies (push) Successful in 9s
Production dependency audit / WEB · production dependencies (push) Successful in 8s
DH V2 CI / Docker / scripts contract (push) Successful in 1m14s
feat(f6.8): harden offline field flow and act documents
2026-09-15 15:56:50 -03:00

84 lines
4.7 KiB
TypeScript

import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import test from 'node:test';
const api = (path: string) => readFileSync(resolve(process.cwd(), path), 'utf8');
const android = (path: string) => readFileSync(resolve(process.cwd(), '..', 'android-app', path), 'utf8');
const web = (path: string) => readFileSync(resolve(process.cwd(), '..', 'web-v2', path), 'utf8');
test('F6.8 persists field mutations locally and retries only when network is available', () => {
const queue = android('app/src/main/java/com/korexlabs/dhinspeccion/data/offline/OfflineQueue.kt');
assert.match(queue, /@Database\(entities = \[PendingMobileOperation::class, OfflineCacheEntry::class\]/);
assert.match(queue, /NetworkType\.CONNECTED/);
assert.match(queue, /OneTimeWorkRequestBuilder<OfflineSyncWorker>/);
for (const operation of [
'VISIT_START', 'VISIT_CLOSE', 'FIELD_ASSET_CREATE', 'FIELD_ASSET_PHOTO',
'ACT_CREATE', 'ACT_ASSET_ENSURE', 'FINDING_CREATE', 'FINDING_PHOTO',
'ACT_RESPONSIBLE', 'ACT_LOCK', 'COMPANY_OUTCOME', 'COMPANY_SIGNATURE', 'ACT_SEAL',
]) assert.match(queue, new RegExp(`"${operation}"`));
});
test('F6.8 isolates offline work and cached inspection data by authenticated user', () => {
const queue = android('app/src/main/java/com/korexlabs/dhinspeccion/data/offline/OfflineQueue.kt');
const mobile = android('app/src/main/java/com/korexlabs/dhinspeccion/data/DhMobile.kt');
const acts = android('app/src/main/java/com/korexlabs/dhinspeccion/data/MobileActs.kt');
const findings = android('app/src/main/java/com/korexlabs/dhinspeccion/data/FieldFindingsMobile.kt');
assert.match(queue, /val ownerUserId: String/);
assert.match(queue, /ownerUserId=:ownerUserId/);
assert.match(queue, /SecureSessionStore/);
assert.match(mobile, /userCacheKey/);
assert.match(acts, /userCacheKey/);
assert.match(findings, /userCacheKey/);
});
test('F6.8 gives offline-created objects idempotent client identities', () => {
const actDto = api('src/inspection-acts/dto/create-inspection-act.dto.ts');
const findingDto = api('src/inspection-findings/dto/create-inspection-finding.dto.ts');
const inventoryDto = api('src/inspection-visits/dto/create-field-inventory.dto.ts');
const migration = api('src/database/migrations/1790128200000-f6-8-offline-evidence-idempotency.ts');
assert.match(actDto, /clientGeneratedId/);
assert.match(findingDto, /clientGeneratedId/);
assert.match(inventoryDto, /clientGeneratedId/);
assert.match(migration, /inspection_finding_evidence/);
assert.match(migration, /asset_field_capture_events/);
assert.equal((migration.match(/client_operation_id/g) ?? []).length >= 6, true);
});
test('F6.8 reviews photos before queueing and never sends the retired ONLINE upload mode', () => {
const photo = android('app/src/main/java/com/korexlabs/dhinspeccion/ui/FieldFindingScreen.kt');
const acts = android('app/src/main/java/com/korexlabs/dhinspeccion/data/MobileActs.kt');
assert.match(photo, /Revisar fotografía/);
assert.match(photo, /Usar esta foto/);
assert.match(photo, /Volver a tomar/);
assert.match(photo, /Eliminar foto/);
assert.doesNotMatch(acts, /uploadMode: String = "ONLINE"/);
assert.match(acts, /uploadMode: String = "IMMEDIATE"/);
});
test('F6.8 makes the real Acta PDF and photographic record first-class in Dashboard', () => {
const controller = api('src/inspection-reports/inspection-reports.controller.ts');
const page = web('src/pages/InspectionActEditorPage.tsx');
const media = web('src/features/inspections/InspectionActMediaPanel.tsx');
assert.match(controller, /inspection-acts\/:actId\/pdf/);
assert.match(api('src/inspection-acts/inspection-acts.controller.ts'), /:id\/field-media/);
assert.match(api('src/inspection-acts/inspection-acts.service.ts'), /capture\.visit_id=act\.visit_id/);
assert.match(page, /Abrir PDF del Acta/);
assert.match(page, /Descargar PDF/);
assert.match(media, /Fotos y Hallazgos/);
assert.match(media, /Evidencia fotográfica/);
assert.match(media, /Fotos tomadas durante esta inspección/);
});
test('F6.8 presents technical families to users as installation and subinstallation types', () => {
const config = web('src/pages/AssetTypesPage.tsx');
const detail = web('src/pages/SimpleInventoryDetailPage.tsx');
const field = api('src/inspection-visits/f3-field-inventory-structure.service.ts');
assert.match(config, /Tipos de Instalación/);
assert.match(config, /Tipos de Subinstalación/);
assert.match(detail, /Tipo de instalación \/ subinstalación/);
assert.match(field, /Tipo de instalación\/subinstalación/);
assert.doesNotMatch(config, /clasificación técnica/i);
assert.doesNotMatch(detail, /clasificación técnica/i);
});