diff --git a/api-v3/test/unit/dashboard-service.test.ts b/api-v3/test/unit/dashboard-service.test.ts index 3281fef..f030d9b 100644 --- a/api-v3/test/unit/dashboard-service.test.ts +++ b/api-v3/test/unit/dashboard-service.test.ts @@ -2,7 +2,7 @@ import { strict as assert } from 'node:assert'; import test from 'node:test'; import { DashboardService } from '../../src/dashboard/dashboard.service'; -test('DashboardService returns numeric counts and recent activity consistently', async () => { +test('DashboardService returns F4 operational counts and recent activity consistently', async () => { const recent = [{ id: 'event-id', occurredAt: new Date('2026-08-13T12:00:00.000Z'), @@ -17,9 +17,8 @@ test('DashboardService returns numeric counts and recent activity consistently', return [{ activeUsers: '4', inactiveUsers: '2', activeSessions: '3', totalAssets: '12', assetsNeedValidation: '4', assetsWithoutGeometry: '6', plannedInspections: '2', - openFindings: '8', awaitingCompanyResponse: '5', overdueCompanyResponses: '2', - companyResponsesDueNext7Days: '1', awaitingVerificationSchedule: '4', - overdueControls: '2', controlsNext30Days: '3', + openFindings: '8', findingsWithoutControlDate: '5', overdueControls: '2', controlsNext30Days: '3', + reportsWorking: '1', reportsOfficialized: '2', sealedActsWithoutReport: '4', }]; } if (sql.includes('FROM inspection_findings finding')) return []; @@ -32,20 +31,20 @@ test('DashboardService returns numeric counts and recent activity consistently', const result = await new DashboardService(dataSource as never).summary(); assert.deepEqual(result.counts, { - activeUsers: 4, - inactiveUsers: 2, - activeSessions: 3, totalAssets: 12, assetsNeedValidation: 4, assetsWithoutGeometry: 6, plannedInspections: 2, + activeUsers: 4, + inactiveUsers: 2, + activeSessions: 3, openFindings: 8, - awaitingCompanyResponse: 5, - overdueCompanyResponses: 2, - companyResponsesDueNext7Days: 1, - awaitingVerificationSchedule: 4, + findingsWithoutControlDate: 5, overdueControls: 2, controlsNext30Days: 3, + reportsWorking: 1, + reportsOfficialized: 2, + sealedActsWithoutReport: 4, }); assert.equal(result.recentAudit, recent); assert.deepEqual(result.upcomingControls, []); diff --git a/api-v3/test/unit/f3-2-mobile-multi-acta.test.ts b/api-v3/test/unit/f3-2-mobile-multi-acta.test.ts index 084934b..5369484 100644 --- a/api-v3/test/unit/f3-2-mobile-multi-acta.test.ts +++ b/api-v3/test/unit/f3-2-mobile-multi-acta.test.ts @@ -11,14 +11,15 @@ test('F3.2/F4 mantiene múltiples Actas con un solo borrador simultáneo por ins assert.match(migration, /WHERE status = 'DRAFT'/); }); -test('F3.2 permite seleccionar el Acta explícitamente al trabajar Hallazgos desde la APK', () => { +test('F3.2/F4 exige seleccionar el Acta explícitamente al crear Hallazgos desde la APK', () => { const controller = read('src/inspection-visits/field-findings.controller.ts'); const service = read('src/inspection-visits/field-findings.service.ts'); const dto = read('src/inspection-visits/dto/create-field-finding.dto.ts'); assert.match(controller, /FieldFindingActQueryDto/); assert.match(controller, /query\.actId/); - assert.match(dto, /actId\?: string/); - assert.match(service, /actSelectionMode: actId \? 'EXPLICIT' : 'LEGACY_SINGLE_DRAFT'/); + assert.match(dto, /actId!: string/); + assert.match(dto, /El Acta es obligatoria/); + assert.match(service, /this\.requireDraftAct\(visitId, dto\.actId\)/); assert.match(service, /WHERE id=\$1::uuid AND visit_id=\$2::uuid/); assert.match(service, /FIELD_FINDING_ACT_NOT_DRAFT/); assert.match(service, /FIELD_FINDING_ASSET_NOT_IN_ACT/); diff --git a/api-v3/test/unit/inspection-acts-dto.test.ts b/api-v3/test/unit/inspection-acts-dto.test.ts index 14297c4..dc06787 100644 --- a/api-v3/test/unit/inspection-acts-dto.test.ts +++ b/api-v3/test/unit/inspection-acts-dto.test.ts @@ -5,18 +5,21 @@ import { plainToInstance } from 'class-transformer'; import { validate } from 'class-validator'; import { CancelInspectionActDto } from '../../src/inspection-acts/dto/cancel-inspection-act.dto'; import { CreateInspectionActDto } from '../../src/inspection-acts/dto/create-inspection-act.dto'; +import { InspectionActUrgency } from '../../src/database/entities'; const ASSET_ID = '16e54e65-60cf-4739-b0d1-ccdd904fbfd5'; -test('inspection act DTO accepts content and selected visit assets', async () => { +test('inspection act DTO accepts urgency, content and selected visit assets', async () => { const dto = plainToInstance(CreateInspectionActDto, { occurredAt: '2026-08-14T15:30:00.000Z', + urgency: InspectionActUrgency.NON_URGENT, title: ' Verificación de instalación ', summary: ' Se inspeccionó el activo seleccionado. ', observations: ' Sin novedades. ', assetIds: [ASSET_ID], }); assert.deepEqual(await validate(dto), []); + assert.equal(dto.urgency, InspectionActUrgency.NON_URGENT); assert.equal(dto.title, 'Verificación de instalación'); assert.equal(dto.summary, 'Se inspeccionó el activo seleccionado.'); assert.equal(dto.observations, 'Sin novedades.'); @@ -25,6 +28,7 @@ test('inspection act DTO accepts content and selected visit assets', async () => test('inspection act DTO rejects duplicate assets', async () => { const dto = plainToInstance(CreateInspectionActDto, { occurredAt: '2026-08-14T15:30:00.000Z', + urgency: InspectionActUrgency.NON_URGENT, title: 'Acta', summary: 'Contenido', assetIds: [ASSET_ID, ASSET_ID], diff --git a/api-v3/test/unit/inspection-closing-authorization.test.ts b/api-v3/test/unit/inspection-closing-authorization.test.ts index 66b8037..3fb20a5 100644 --- a/api-v3/test/unit/inspection-closing-authorization.test.ts +++ b/api-v3/test/unit/inspection-closing-authorization.test.ts @@ -12,14 +12,14 @@ function permissionFor(controller: object, method: string): string[] { return Reflect.getMetadata(REQUIRED_PERMISSIONS_KEY, handler) as string[]; } -test('D5 closing endpoints separate read, prepare, sign and close permissions', () => { +test('F4 closing endpoints separate read, lock, sign and seal permissions without reopen', () => { assert.deepEqual(permissionFor(InspectionClosingController.prototype, 'get'), ['inspection_closure.read']); assert.deepEqual(permissionFor(InspectionClosingController.prototype, 'responsible'), ['inspection_closure.prepare']); - assert.deepEqual(permissionFor(InspectionClosingController.prototype, 'ready'), ['inspection_closure.prepare']); - assert.deepEqual(permissionFor(InspectionClosingController.prototype, 'reopen'), ['inspection_closure.prepare']); + assert.deepEqual(permissionFor(InspectionClosingController.prototype, 'lock'), ['inspection_closure.prepare']); + assert.equal('reopen' in InspectionClosingController.prototype, false); assert.deepEqual(permissionFor(InspectionClosingController.prototype, 'inspectorSignature'), ['inspection_closure.sign']); assert.deepEqual(permissionFor(InspectionClosingController.prototype, 'companySignature'), ['inspection_closure.sign']); assert.deepEqual(permissionFor(InspectionClosingController.prototype, 'companyOutcome'), ['inspection_closure.sign']); - assert.deepEqual(permissionFor(InspectionClosingController.prototype, 'close'), ['inspection_closure.close']); + assert.deepEqual(permissionFor(InspectionClosingController.prototype, 'seal'), ['inspection_closure.close']); assert.deepEqual(permissionFor(InspectionSignatureContentController.prototype, 'content'), ['inspection_closure.read']); }); diff --git a/api-v3/test/unit/inspection-findings-authorization.test.ts b/api-v3/test/unit/inspection-findings-authorization.test.ts index b4b4257..25e2737 100644 --- a/api-v3/test/unit/inspection-findings-authorization.test.ts +++ b/api-v3/test/unit/inspection-findings-authorization.test.ts @@ -13,40 +13,16 @@ function permissionFor(controller: object, method: string): string[] { return Reflect.getMetadata(REQUIRED_PERMISSIONS_KEY, handler) as string[]; } -test('finding endpoints separate catalog, content, follow-up and closure permissions', () => { - assert.deepEqual(permissionFor(FindingCatalogController.prototype, 'list'), [ - 'finding_catalog.read', - ]); - for (const method of [ - 'adminList', - 'createCategory', - 'updateCategory', - 'createItem', - 'updateItem', - ]) { - assert.deepEqual(permissionFor(FindingCatalogController.prototype, method), [ - 'finding_catalog.manage', - ]); +test('F4 finding endpoints separate catalog, content and closure while mutable follow-up is retired', () => { + assert.deepEqual(permissionFor(FindingCatalogController.prototype, 'list'), ['finding_catalog.read']); + for (const method of ['adminList', 'createCategory', 'updateCategory', 'createItem', 'updateItem']) { + assert.deepEqual(permissionFor(FindingCatalogController.prototype, method), ['finding_catalog.manage']); } - assert.deepEqual(permissionFor(InspectionActFindingsController.prototype, 'list'), [ - 'inspection_findings.read', - ]); - assert.deepEqual(permissionFor(InspectionActFindingsController.prototype, 'create'), [ - 'inspection_findings.create', - ]); - assert.deepEqual(permissionFor(InspectionFindingsController.prototype, 'list'), [ - 'inspection_findings.read', - ]); - assert.deepEqual(permissionFor(InspectionFindingsController.prototype, 'get'), [ - 'inspection_findings.read', - ]); - assert.deepEqual(permissionFor(InspectionFindingsController.prototype, 'update'), [ - 'inspection_findings.update', - ]); - assert.deepEqual(permissionFor(InspectionFindingsController.prototype, 'updateFollowUp'), [ - 'inspection_findings.follow_up', - ]); - assert.deepEqual(permissionFor(InspectionFindingsController.prototype, 'close'), [ - 'inspection_findings.close', - ]); + assert.deepEqual(permissionFor(InspectionActFindingsController.prototype, 'list'), ['inspection_findings.read']); + assert.deepEqual(permissionFor(InspectionActFindingsController.prototype, 'create'), ['inspection_findings.create']); + assert.deepEqual(permissionFor(InspectionFindingsController.prototype, 'list'), ['inspection_findings.read']); + assert.deepEqual(permissionFor(InspectionFindingsController.prototype, 'get'), ['inspection_findings.read']); + assert.deepEqual(permissionFor(InspectionFindingsController.prototype, 'update'), ['inspection_findings.update']); + assert.equal('updateFollowUp' in InspectionFindingsController.prototype, false); + assert.deepEqual(permissionFor(InspectionFindingsController.prototype, 'close'), ['inspection_findings.close']); }); diff --git a/api-v3/test/unit/inspection-findings-office-workflow.test.ts b/api-v3/test/unit/inspection-findings-office-workflow.test.ts index b07b812..b14d607 100644 --- a/api-v3/test/unit/inspection-findings-office-workflow.test.ts +++ b/api-v3/test/unit/inspection-findings-office-workflow.test.ts @@ -13,9 +13,9 @@ test('D5.3.13 separates company response due dates from verification due dates', assert.match(source, /verificationOverdue/); }); -test('D5.3.13 exposes a global office queue and controlled finding closure', () => { +test('D5.3.13/F4 exposes the global worklist and controlled finding closure', () => { assert.match(controller, /@Get\(\)/); - assert.match(controller, /this\.findings\.listGlobal\(query\)/); + assert.match(controller, /this\.worklist\.list\(query\)/); assert.match(controller, /@Patch\(':id\/close'\)/); assert.match(source, /finding\.status = InspectionFindingStatus\.CLOSED/); assert.match(source, /finding\.closureNotes = dto\.closureNotes/); diff --git a/api-v3/test/unit/inspection-visits-authorization.test.ts b/api-v3/test/unit/inspection-visits-authorization.test.ts index 450d2a7..b8b1c4c 100644 --- a/api-v3/test/unit/inspection-visits-authorization.test.ts +++ b/api-v3/test/unit/inspection-visits-authorization.test.ts @@ -10,7 +10,7 @@ function permissionFor(method: string): string[] { return Reflect.getMetadata(REQUIRED_PERMISSIONS_KEY, handler) as string[]; } -test('inspection visit endpoints separate reading, planning, assignment and execution', () => { +test('F4 inspection endpoints separate reading, planning, assignment and execution', () => { assert.deepEqual(permissionFor('list'), ['inspections.read']); assert.deepEqual(permissionFor('get'), ['inspections.read']); assert.deepEqual(permissionFor('assignees'), ['inspections.assign']); @@ -18,7 +18,9 @@ test('inspection visit endpoints separate reading, planning, assignment and exec assert.deepEqual(permissionFor('update'), ['inspections.manage']); assert.deepEqual(permissionFor('replaceAssets'), ['inspections.manage']); assert.deepEqual(permissionFor('replaceTeam'), ['inspections.assign']); - assert.deepEqual(permissionFor('changeStatus'), ['inspections.manage']); + assert.deepEqual(permissionFor('plan'), ['inspections.manage']); + assert.deepEqual(permissionFor('unplan'), ['inspections.manage']); assert.deepEqual(permissionFor('start'), ['inspections.execute']); assert.deepEqual(permissionFor('close'), ['inspections.execute']); + assert.deepEqual(permissionFor('cancel'), ['inspections.manage']); }); diff --git a/api-v3/test/unit/mobile-inspector-integration.test.ts b/api-v3/test/unit/mobile-inspector-integration.test.ts index 40917e1..7a970bc 100644 --- a/api-v3/test/unit/mobile-inspector-integration.test.ts +++ b/api-v3/test/unit/mobile-inspector-integration.test.ts @@ -11,15 +11,15 @@ function policyCalls(value: string): number { return value.match(/assertMobileInspector\(principal\);/g)?.length ?? 0; } -test('D5.1 applies the mobile-inspector policy to every field operation', async () => { - const [visits, acts, findings, evidence, closing] = await Promise.all([ - source('inspection-visits/inspection-visits.service.ts'), +test('D5.1/F4 applies the mobile-inspector policy to every field operation', async () => { + const [lifecycle, acts, findings, evidence, closing] = await Promise.all([ + source('inspection-visits/inspection-visit-lifecycle.service.ts'), source('inspection-acts/inspection-acts.service.ts'), source('inspection-findings/inspection-findings.service.ts'), source('inspection-findings/inspection-evidence.service.ts'), source('inspection-closing/inspection-closing.service.ts'), ]); - assert.equal(policyCalls(visits), 2); + assert.equal(policyCalls(lifecycle), 2); assert.equal(policyCalls(acts), 3); assert.equal(policyCalls(findings), 2); assert.equal(policyCalls(evidence), 1); @@ -34,9 +34,7 @@ test('D5.1 leaves company follow-up outside the field-operation gate', async () }); test('D5.1 migration grants operational permissions only to inspector', async () => { - const migration = await source( - 'database/migrations/1787331600000-phase-d5-1-mobile-inspection-policy.ts', - ); + const migration = await source('database/migrations/1787331600000-phase-d5-1-mobile-inspection-policy.ts'); assert.match(migration, /role\.code <> 'inspector'/); assert.match(migration, /role\.code = 'inspector'/); assert.doesNotMatch(migration, /inspection_findings\.follow_up/); diff --git a/api-v3/test/unit/phase-d5-3-15-document-center.test.ts b/api-v3/test/unit/phase-d5-3-15-document-center.test.ts index 948a4ac..4b327ce 100644 --- a/api-v3/test/unit/phase-d5-3-15-document-center.test.ts +++ b/api-v3/test/unit/phase-d5-3-15-document-center.test.ts @@ -19,7 +19,8 @@ test('D5.3.15/F4 exposes global acts and report emission after an Act is SEALED' test('D5.3.15/F4 freezes the immutable sealed Act snapshot as the INF source', () => { assert.match(reportService, /assertMobileInspector\(principal\)/); assert.match(reportService, /final_snapshot AS "finalSnapshot"/); - assert.match(reportService, /DocumentSequenceType\.REPORT/); + assert.match(reportService, /const reportNumber = act\.actNumber/); + assert.match(reportService, /act\.code\.replace\(\/\^ACT-\/, 'INF-'\)/); assert.match(reportService, /sha256CanonicalJson\(frozenSnapshot\)/); assert.match(reportService, /InspectionReportStatus\.WORKING/); assert.match(reportService, /INSPECTION_REPORT_GENERATED/); diff --git a/api-v3/test/unit/phase-d5-3-16-verification-planning.test.ts b/api-v3/test/unit/phase-d5-3-16-verification-planning.test.ts index 2e2bcda..c80118c 100644 --- a/api-v3/test/unit/phase-d5-3-16-verification-planning.test.ts +++ b/api-v3/test/unit/phase-d5-3-16-verification-planning.test.ts @@ -7,10 +7,11 @@ const service = readFileSync(resolve(process.cwd(), 'src/inspection-verification const migration = readFileSync(resolve(process.cwd(), 'src/database/migrations/1788112800000-phase-d5-3-16-verification-planning.ts'), 'utf8'); const visitService = readFileSync(resolve(process.cwd(), 'src/inspection-visits/inspection-visits.service.ts'), 'utf8'); -test('D5.3.16 builds the verification queue from the second finding deadline', () => { - assert.match(service, /company_response_received_on IS NOT NULL/); +test('D5.3.16/F4 builds the verification queue from the next control date and unresolved latest result', () => { + assert.doesNotMatch(service, /company_response_received_on IS NOT NULL/); assert.match(service, /finding\.next_control_on IS NOT NULL/); - assert.match(service, /verification_visit\.id IS NULL/); + assert.match(service, /latest_verification\.outcome/); + assert.match(service, /<> 'RESOLVED'/); assert.match(service, /overdueUnplanned/); }); diff --git a/api-v3/test/unit/phase-d5-3-20-automatic-word-report.test.ts b/api-v3/test/unit/phase-d5-3-20-automatic-word-report.test.ts index fd6b2d9..61a5fb1 100644 --- a/api-v3/test/unit/phase-d5-3-20-automatic-word-report.test.ts +++ b/api-v3/test/unit/phase-d5-3-20-automatic-word-report.test.ts @@ -24,7 +24,7 @@ test('D5.3.20 stores an integrity-checked Word artifact without changing the fro assert.match(migration, /word_sha256 char\(64\)/); assert.match(migration, /word_generated_at timestamptz/); assert.match(wordService, /createHash\('sha256'\)/); - assert.match(wordService, /word_status = 'READY'/); + assert.match(wordService, /word_status\s*=\s*'READY'/); assert.match(controller, /@Get\(':id\/word'\)/); }); diff --git a/api-v3/test/unit/phase-d5-4-contextual-finding-catalog.test.ts b/api-v3/test/unit/phase-d5-4-contextual-finding-catalog.test.ts index 0369f07..2a0c907 100644 --- a/api-v3/test/unit/phase-d5-4-contextual-finding-catalog.test.ts +++ b/api-v3/test/unit/phase-d5-4-contextual-finding-catalog.test.ts @@ -40,7 +40,6 @@ test('D5.4 stores suggested and actual severity independently on the 1 to 10 sca assert.match(createFindingDto, /severity\?: number/); assert.match(findingsService, /suggestedSeverity: catalog\?\.suggestedSeverity/); assert.match(findingsService, /severity: dto\.severity \?\? catalog\?\.suggestedSeverity/); - assert.match(closingService, /finding\.suggested_severity AS "suggestedSeverity"/); assert.match(closingService, /finding\.severity/); assert.match(wordBuilder, /'Gravedad'/); }); diff --git a/api-v3/test/unit/phase-d5-5-web-planning-checklist.test.ts b/api-v3/test/unit/phase-d5-5-web-planning-checklist.test.ts index 90d58ea..89b011c 100644 --- a/api-v3/test/unit/phase-d5-5-web-planning-checklist.test.ts +++ b/api-v3/test/unit/phase-d5-5-web-planning-checklist.test.ts @@ -45,22 +45,22 @@ test('D5.5 treats manual additions as preventive and requires an auditable reaso assert.match(dto, /@MinLength\(10\)/); }); -test('D5.5 keeps verification visits inside the same Area Operator planning contract and start remains APK-only', () => { +test('D5.5/F4 keeps verification visits inside the Area Operator contract and start remains APK-only', () => { const verification = read('src/inspection-verifications/inspection-verifications.service.ts'); - const visits = read('src/inspection-visits/inspection-visits.service.ts'); + const lifecycle = read('src/inspection-visits/inspection-visit-lifecycle.service.ts'); assert.match(verification, /operationalAreaId: areaId/); assert.match(verification, /operatorCompanyId: companyId/); assert.match(verification, /planning_source = 'VERIFICATION'/); - assert.match(visits, /assertMobileInspector\(principal\)/); - assert.match(visits, /INSPECTION_CHECKLIST_REQUIRED/); + assert.match(lifecycle, /assertMobileInspector\(principal\)/); + assert.match(lifecycle, /INSPECTION_CHECKLIST_REQUIRED/); }); -test('D5.5 confirmation requires a reviewed current checklist and keeps visit start under mobile inspector policy', () => { - const visits = read('src/inspection-visits/inspection-visits.service.ts'); +test('D5.5/F4 requires a current checklist before planning or start and keeps start under mobile policy', () => { + const lifecycle = read('src/inspection-visits/inspection-visit-lifecycle.service.ts'); const policy = read('src/inspection-operations/mobile-inspector-policy.ts'); - assert.match(visits, /INSPECTION_CHECKLIST_REQUIRED/); - assert.match(visits, /checklistGeneratedAt/); - assert.match(visits, /assertMobileInspector\(principal\)/); + assert.match(lifecycle, /INSPECTION_CHECKLIST_REQUIRED/); + assert.match(lifecycle, /checklistGeneratedAt/); + assert.match(lifecycle, /assertMobileInspector\(principal\)/); assert.match(policy, /transport !== 'bearer'/); assert.match(policy, /includes\('inspector'\)/); }); diff --git a/api-v3/test/unit/phase-d5-6-4-quick-inspection-planning.test.ts b/api-v3/test/unit/phase-d5-6-4-quick-inspection-planning.test.ts index c7f2698..52d9f6b 100644 --- a/api-v3/test/unit/phase-d5-6-4-quick-inspection-planning.test.ts +++ b/api-v3/test/unit/phase-d5-6-4-quick-inspection-planning.test.ts @@ -6,14 +6,18 @@ import test from 'node:test'; const root = process.cwd(); const read = (relative: string) => fs.readFileSync(path.join(root, relative), 'utf8'); -test('D5.6.4 generates inspection codes server-side with an annual transactional sequence', () => { +test('D5.6.4/F4 generates inspection codes server-side with an annual transactional sequence', () => { const generator = read('src/inspection-visits/inspection-visit-code.ts'); - assert.match(generator, /INS-\$\{year\}-\$\{String\(sequence\)\.padStart\(6, '0'\)\}/); assert.match(generator, /pg_advisory_xact_lock/); assert.match(generator, /America\/Argentina\/Mendoza/); + assert.match(generator, /padStart\(5, '0'\)/); + assert.match(generator, /return `INSP-/); + assert.match(generator, /dateRow\.day/); + assert.match(generator, /dateRow\.month/); + assert.match(generator, /dateRow\.short_year/); }); -test('D5.6.4 creates one inspection from Area Operator start and lead inspector without manual title or end date', () => { +test('D5.6.4/F4 creates one inspection from Area Operator, start and lead inspector without legacy title or end date', () => { const dto = read('src/inspection-visits/dto/create-inspection-visit.dto.ts'); const service = read('src/inspection-visits/inspection-visits.service.ts'); assert.doesNotMatch(dto, /code!/); @@ -23,8 +27,9 @@ test('D5.6.4 creates one inspection from Area Operator start and lead inspector assert.match(dto, /operatorCompanyId!/); assert.match(dto, /plannedStartAt!/); assert.match(dto, /leadInspectorUserId!/); - assert.match(service, /title: code/); - assert.match(service, /plannedEndAt: null/); + assert.match(service, /const code = await nextInspectionVisitCode\(manager, plannedStartAt\)/); + assert.doesNotMatch(service, /title: code/); + assert.doesNotMatch(service, /plannedEndAt/); }); test('D5.6.4 assigns the lead inspector and generates the checklist in the same creation transaction', () => { @@ -34,10 +39,10 @@ test('D5.6.4 assigns the lead inspector and generates the checklist in the same assert.match(service, /await this\.generateChecklistInternal\(manager, visit, principal\.userId\)/); }); -test('D5.6.4 uses the same inspection numbering and start-only planning for verification visits', () => { +test('D5.6.4/F4 uses the same inspection numbering and start-only planning for verification visits', () => { const dto = read('src/inspection-verifications/dto/plan-verification-visit.dto.ts'); const service = read('src/inspection-verifications/inspection-verifications.service.ts'); assert.doesNotMatch(dto, /plannedEndAt/); assert.match(service, /nextInspectionVisitCode\(manager, plannedStartAt\)/); - assert.match(service, /plannedEndAt: null/); + assert.doesNotMatch(service, /plannedEndAt/); });