import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import test from 'node:test'; const root = process.cwd(); const read = (relative: string) => fs.readFileSync(path.join(root, relative), 'utf8'); const migration = read('src/database/migrations/1790000700000-f4-align-asset-version-function-change.ts'); const workflow = read('src/inspection-reports/inspection-report-workflow.service.ts'); const word = read('src/inspection-reports/inspection-report-word.service.ts'); const controller = read('src/inspection-reports/inspection-reports.controller.ts'); const compose = read('../docker-compose.yml'); const webApi = read('../web-v2/src/lib/reportWorkflowApi.ts'); const reportPage = read('../web-v2/src/pages/ReportDetailPage.tsx'); test('F4 physically permits FUNCTION_CHANGED asset versions with a reversible downgrade', () => { const up = migration.slice(migration.indexOf(' public async up('), migration.indexOf(' public async down(')); const down = migration.slice(migration.indexOf(' public async down(')); assert.match(up, /FUNCTION_CHANGED/); assert.match(down, /SET change_type='UPDATED'[\s\S]*WHERE change_type='FUNCTION_CHANGED'/); }); test('F4 GEDO and follow-up uploads stay under the persisted asset-media volume', () => { assert.match(workflow, /\?\? '\/app\/storage\/asset-media\/inspection-reports'/); assert.match(compose, /INSPECTION_REPORT_UPLOAD_ROOT: \/app\/storage\/asset-media\/inspection-reports/); assert.match(compose, /dhv2_asset_media:\/app\/storage\/asset-media/); }); test('F4 narrative edits invalidate the current generated Word without overwriting its historical file', () => { const update = workflow.slice(workflow.indexOf(' async updateNarrative('), workflow.indexOf(' async officialize(')); assert.match(update, /word_status=CASE[\s\S]*THEN 'PENDING'/); assert.match(update, /word_stored_name=CASE[\s\S]*THEN NULL/); assert.match(update, /word_sha256=CASE[\s\S]*THEN NULL/); assert.match(word, /built\.sha256\.slice\(0, 16\)/); }); test('F4 exposes the immutable official GEDO PDF through a protected integrity-checked download', () => { assert.match(controller, /@Get\(':id\/gedo-pdf'\)[\s\S]*@RequirePermissions\('inspection_reports\.read'\)/); assert.match(controller, /this\.workflow\.officialPdfContent\(id\)/); assert.match(workflow, /async officialPdfContent\(/); assert.match(workflow, /fileStat\.size !== row\.sizeBytes/); assert.match(workflow, /createHash\('sha256'\)\.update\(buffer\)\.digest\('hex'\)/); assert.match(webApi, /inspectionReportGedoPdfDownloadUrl/); assert.match(reportPage, /Descargar PDF oficial/); });