72 lines
4.7 KiB
JavaScript
72 lines
4.7 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
|
|
export async function verify(vite, dataset, sheet) {
|
|
const exports = await vite.ssrLoadModule('/src/exports/work-view-export.ts');
|
|
const { genericProfile } = await vite.ssrLoadModule('/src/profiles/generic/generic-profile.ts');
|
|
const { filterAndSortRecords } = await vite.ssrLoadModule('/src/features/search-filter/filter-records.ts');
|
|
const { recordResultSheets } = await vite.ssrLoadModule('/src/features/linked-results/record-results.ts');
|
|
const { resultSectionsFor } = await vite.ssrLoadModule('/src/features/linked-results/result-message.ts');
|
|
const { toolIdForRecord } = await vite.ssrLoadModule('/src/importers/status-normalization.ts');
|
|
const options = { dataset, activeProfile: genericProfile, search: '', statusFilter: 'deviation', facetFilters: {},
|
|
sortColumn: 'all', sortDirection: 'asc', actionOnly: false, reviews: {} };
|
|
const counts = [];
|
|
for (const statusFilter of ['deviation', 'all']) {
|
|
const records = filterAndSortRecords(sheet, { ...options, statusFilter });
|
|
const report = exports.buildWorkViewExport({ dataset, sheet, records, generatedAt: new Date().toISOString() });
|
|
assert.deepEqual(report.entries.map(entry => entry.id), records.map(record => record.id));
|
|
let cells = 0;
|
|
let sources = 0;
|
|
let sections = 0;
|
|
for (const [index, record] of records.entries()) {
|
|
const entry = report.entries[index];
|
|
const tool = toolIdForRecord(record, sheet.toolId ?? dataset.profileId);
|
|
const linked = recordResultSheets(record, dataset.sheets, sheet.id, tool);
|
|
assert.deepEqual(entry.resultSheetIds, linked.map(item => item.id));
|
|
assert.equal(entry.resultRowCount, linked.reduce((sum, item) => sum + item.analysis.records.length, 0));
|
|
const actual = entry.observed.map(block => block.value).join('\n');
|
|
for (const item of linked) for (const row of item.analysis.records) for (const field of row.fields) {
|
|
assert.ok(actual.includes(`${field.label}: ${field.value}`), 'Lost result cell');
|
|
cells++;
|
|
}
|
|
for (const section of resultSectionsFor(record, tool)) {
|
|
assert.ok(entry.observed.some(block => block.value === section.value), 'Lost result section');
|
|
sections++;
|
|
}
|
|
for (const source of record.policySourceEvidence ?? []) {
|
|
assert.ok(actual.includes(source.id), 'Lost GPO observation');
|
|
assert.ok(actual.includes(source.sourceReference), 'Lost GPO evidence path');
|
|
sources++;
|
|
}
|
|
}
|
|
const csv = exports.renderWorkViewCsv(report);
|
|
const html = exports.renderWorkViewHtml(report);
|
|
const md = exports.renderWorkViewMarkdown(report);
|
|
const { read, utils } = await import('xlsx');
|
|
// Decode the UTF-8 BOM before passing a JS string to SheetJS (Excel handles it).
|
|
const book = read(csv.replace(/^\ufeff/, ''), { type: 'string', raw: true, FS: ';' });
|
|
const rows = utils.sheet_to_json(book.Sheets[book.SheetNames[0]], { header: 1, raw: true });
|
|
const expectedCsvRows = exports.workViewCsvRows(report);
|
|
assert.equal(rows.length, expectedCsvRows.length, 'CSV row count');
|
|
const differences = [];
|
|
rows.forEach((row, index) => row.forEach((cell, col) => {
|
|
const expected = expectedCsvRows[index][col];
|
|
if (cell !== expected) {
|
|
let first = 0;
|
|
while (first < Math.min(cell.length, expected.length) && cell[first] === expected[first]) first++;
|
|
differences.push({ row: index, column: col, actualLength: cell.length, expectedLength: expected.length,
|
|
firstDifference: first, actualChar: cell.charCodeAt(first), expectedChar: expected.charCodeAt(first) });
|
|
}
|
|
}));
|
|
if (differences.length) { console.log(JSON.stringify({ csvDifferences: differences.slice(0, 5) })); throw new Error('CSV content round-trip differs'); }
|
|
assert.ok(rows.every(row => row.length === 2), 'Not exactly two CSV columns');
|
|
assert.equal((html.match(/<article class="finding">/g) ?? []).length, records.length);
|
|
assert.equal((md.match(/^\| /gm) ?? []).length, records.length + 2);
|
|
counts.push({ statusFilter, records: records.length, csvContinuationRows: rows.length - records.length - 1, resultRows: report.entries.reduce((sum, item) => sum + item.resultRowCount, 0),
|
|
checkedResultCells: cells, resultSections: sections, gpoObservations: sources,
|
|
missingResultWarnings: report.entries.filter(item => item.observed.some(block => /nicht geladen/.test(block.value))).length,
|
|
oversizedCsvCells: rows.flat().filter(cell => String(cell).length > 32767).length,
|
|
bytes: { csv: Buffer.byteLength(csv), html: Buffer.byteLength(html), md: Buffer.byteLength(md) } });
|
|
}
|
|
console.log(JSON.stringify({ verification: 'passed', counts }, null, 2));
|
|
}
|