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(/
/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)); }