Files
Investidor-V2/tests/frontend/dossier-export-recovery.test.mjs
T

142 lines
5.2 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import {
DossierExportGate,
dossierExportFailure,
prepareDossierAttachments,
} from '../../resources/js/lib/dossier-export-state.ts';
import {
createProjectDossierReport,
generateProjectDossierDocx,
createProjectDossierPrintHtml,
generateProjectDossierPackage,
} from '../../resources/js/lib/project-dossier-report.ts';
void test('concurrent export is ignored; failed generation unlocks one retry without mutating inputs', async () => {
const gate = new DossierExportGate();
const draft = {
project_title: 'SINTÉTICO QA',
project_summary: 'Pressupostos declarados; não é resultado fiscal.',
};
const saved = JSON.stringify(draft);
let release;
let starts = 0;
const first = gate.run(async () => {
starts++;
await new Promise((resolve) => {
release = resolve;
});
throw new Error('Library failure');
});
assert.equal(
await gate.run(async () => {
starts++;
}),
false,
);
release();
await assert.rejects(first, /Library failure/);
assert.equal(
await gate.run(async () => {
starts++;
}),
true,
);
assert.equal(starts, 2);
assert.equal(JSON.stringify(draft), saved);
});
void test('all operations show Portuguese recovery text while retaining technical details in diagnostics', () => {
for (const operation of ['word', 'package', 'pdf', 'csv', 'attachments']) {
const error = new Error('English internals / private stack');
const logs = [];
const text = dossierExportFailure(operation, error, (...args) =>
logs.push(args),
);
assert.match(text, /Não foi possível/);
assert.match(text, /foram mantidos.*Tente novamente/);
assert.doesNotMatch(text, /English internals|private stack/);
assert.equal(logs.length, 1);
assert.equal(logs[0][1].cause, error);
assert.ok(text.includes(logs[0][1].reference));
}
});
void test('attachment read failure and size refusal preserve existing selection; retry deduplicates bytes and keeps captions', async () => {
const file = new File(['SINTÉTICO QA'], 'qa.txt', { type: 'text/plain' });
const current = [{ file, include: false, caption: 'Legenda conservada' }];
const broken = new File(['another'], 'broken.txt');
broken.arrayBuffer = async () => {
throw new Error('OS read failed');
};
await assert.rejects(
prepareDossierAttachments(current, [broken]),
/OS read failed/,
);
assert.equal(current.length, 1);
const tooLarge = new File([new Uint8Array(16 * 1024 * 1024)], 'large.txt');
const refused = await prepareDossierAttachments(current, [tooLarge]);
assert.equal(refused.attachments, current);
assert.match(refused.problem, /15 MB/);
const result = await prepareDossierAttachments(current, [
new File(['SINTÉTICO QA'], 'qa.txt'),
new File(['distinct'], 'qa.txt'),
]);
assert.equal(result.duplicateCount, 1);
assert.equal(result.attachments.length, 2);
assert.equal(result.attachments[0], current[0]);
assert.equal(result.attachments[0].caption, 'Legenda conservada');
assert.equal(result.attachments[0].include, false);
});
void test('actual Word PDF and ZIP generators recover after a transient attachment read failure; archive has one original', async () => {
const model = createProjectDossierReport(
{ project_title: 'QA recuperação' },
{ selectedSections: new Set(['summary', 'annexes']) },
);
const file = new File(['Texto QA'], 'qa.txt', { type: 'text/plain' });
const original = file.text.bind(file);
let fail = false;
file.text = async () => {
if (fail) {
fail = false;
throw new Error('Transient attachment read failure');
}
return original();
};
const attachments = [{ file, include: true, caption: '' }];
for (const generate of [
() => generateProjectDossierDocx(model, attachments, true),
() => createProjectDossierPrintHtml(model, attachments, true),
]) {
fail = true;
await assert.rejects(generate, /Transient attachment/);
assert.ok(await generate());
assert.equal(attachments.length, 1);
}
const word = await generateProjectDossierDocx(model, [], false);
let broken = true;
const { DossierExportGate: Gate } =
await import('../../resources/js/lib/dossier-export-state.ts');
const gate = new Gate();
await assert.rejects(
gate.run(async () => {
if (broken) throw new Error('ZIP write fault');
}),
/ZIP write fault/,
);
broken = false;
let zip;
await gate.run(async () => {
zip = await generateProjectDossierPackage(word, attachments, true);
});
const { default: JSZip } = await import('jszip');
const archive = await JSZip.loadAsync(await zip.arrayBuffer());
assert.deepEqual(
Object.keys(archive.files).filter(
(name) => name.startsWith('Anexos/') && !archive.files[name].dir,
),
['Anexos/qa.txt'],
);
});