fix: close final F4 review gaps

This commit is contained in:
github-actions[bot]
2026-09-08 18:22:02 +00:00
parent 969ffddc76
commit 5dc7ea037d
9 changed files with 182 additions and 137 deletions
@@ -0,0 +1,46 @@
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/);
});