Files
Converge/scripts/test-report-leak.mjs
Chris Olson 93007a1053 Sprint 2 UI and brand refresh
Workbench: ramp-adjusted headline (TCO, net run-rate, NPV, IRR, payback),
financial model panel with internal-only investment, year-by-year cash flow,
scenario panel (presets + custom, presented scenario, report comparison),
overlap heat map, shelfware and expansion panel, editable implementation
timeline (drag, resize, row editor, dependencies, reset), version history
with compare and restore, deal status, customer logo.

Deliverables: report, Word and new PowerPoint deck present the chosen
scenario with net figures only; investment and dossier entitlements never
itemized (covered by the leak suite). Exports and printing snapshot
automatically.

New pages: Portfolio dashboard, Settings (company name, logo, accent).
Knowledge base: duplicate queue with merge, confidence and sourcing,
TCO defaults, needs-review filter.

Brand: VMware by Broadcom palette per Brand Guidelines.pdf; Darkmode navy
dark theme; AA-safe accent ink; business units remapped to palette.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 15:41:15 -04:00

107 lines
5.7 KiB
JavaScript

/**
* The account dossier holds internal pricing strategy, competitive risk and
* personal data. None of it may reach a customer deliverable.
*
* Self-contained: builds its own assessment, imports the sample dossier through
* the real endpoint, asserts on the redacted projection and the generated Word
* file, then removes itself.
*
* (The rendered report page is verified in a browser; its server-rendered shell
* carries no data, so asserting on that HTML would pass vacuously.)
*/
import { readFileSync, writeFileSync } from 'node:fs';
import { execFileSync } from 'node:child_process';
const B = 'http://127.0.0.1:3500';
const json = (u, o) => fetch(B + u, o).then(r => r.json());
const send = (u, m, b) => json(u, { method: m, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(b) });
let pass = 0, fail = 0;
const t = (n, ok, x = '') => { ok ? (pass++, console.log(' PASS', n)) : (fail++, console.log(' FAIL', n, x)); };
const text = readFileSync(new URL('./fixtures/dossier-sample.txt', import.meta.url), 'utf8');
const SOURCE = 'https://docs.google.com/document/d/LEAKTEST/edit';
const a = await send('/api/assessments', 'POST', { customerName: 'Redaction Test Co' });
try {
const imported = await send('/api/assessments/x/intel', 'POST', { text, sourceUrl: SOURCE });
t('dossier parsed for the test', imported.proposals.length > 0 && !!imported.intel.company);
a.accountIntel = imported.intel;
a.candidates = imported.proposals.slice(0, 3).map((p, i) => ({
id: `c-${i}`, incumbentVendor: p.vendor, incumbentProduct: p.product, domain: p.domain,
productIds: p.productIds, annualSpend: 500000, units: 0, unitLabel: '', renewalDate: '',
capability: p.capability, switchEase: p.switchEase, timeToRoiMonths: p.timeToRoiMonths,
savingsPct: p.savingsPct, migrationCost: 50000, strategicValue: 3,
risks: '', notes: `From account dossier — ${p.evidence}`, included: true,
}));
await send(`/api/assessments/${a.id}`, 'PUT', a);
const full = await json(`/api/assessments/${a.id}`);
t('stored record keeps the dossier', !!full.accountIntel);
t('stored record keeps internal notes', full.candidates.every(c => c.notes));
const safe = await json(`/api/assessments/${a.id}/report`);
t('projection drops the dossier', safe.accountIntel === null);
t('projection drops internal notes', safe.candidates.every(c => c.notes === ''));
t('projection keeps every candidate', safe.candidates.length === full.candidates.length);
t('projection keeps the mappings', safe.candidates.every((c, i) => c.productIds.join() === full.candidates[i].productIds.join()));
t('projection keeps the spend figures', safe.candidates.every((c, i) => c.annualSpend === full.candidates[i].annualSpend));
// A .docx is a ZIP: the bytes must be inflated or a text search passes vacuously.
const buf = Buffer.from(await (await fetch(`${B}/api/assessments/${a.id}/docx`)).arrayBuffer());
const tmp = new URL('./.leak-test.docx', import.meta.url);
writeFileSync(tmp, buf);
const docx = execFileSync('unzip', ['-p', tmp.pathname, 'word/document.xml'], { maxBuffer: 32e6 }).toString('utf8');
t('Word file is a readable docx', docx.includes('<w:document') && docx.length > 2000);
const safeText = JSON.stringify(safe);
const secrets = [
SOURCE, 'docs.google.com', 'Tier 1 / Strategic', 'Renewal Risk', 'Intelligence Score',
'TechDirector', 'Shelfware', 'Tech Debt Signals', 'AM-SOUTHEAST', 'From account dossier',
'Everpure', 'Workload Distribution',
];
for (const s of secrets) {
const label = s.length > 30 ? s.slice(0, 30) + '…' : s;
t(`projection omits "${label}"`, !safeText.includes(s));
t(`Word omits "${label}"`, !docx.includes(s));
}
t('Word still names the customer', docx.includes('Redaction Test Co'));
t('Word still carries a mapped product', /Avi Load Balancer|VCF Networking|Layer7/.test(docx));
execFileSync('rm', ['-f', tmp.pathname]);
/* ---- our pricing and dossier entitlements stay internal (sprint 2) ---- */
const PRICE = 7_654_321, SERVICES = 1_234_567;
const fmt = (n) => '$' + n.toLocaleString('en-US');
const priced = await json(`/api/assessments/${a.id}`);
priced.finance = { discountRate: 10, fteCost: 150000, investment: { annual: PRICE, services: SERVICES } };
priced.candidates[0].alreadyEntitled = 'VMware Avi Load Balancer';
await send(`/api/assessments/${a.id}`, 'PUT', priced);
const deckBuf = Buffer.from(await (await fetch(`${B}/api/assessments/${a.id}/deck`)).arrayBuffer());
const deckTmp = new URL('./.leak-test.pptx', import.meta.url);
writeFileSync(deckTmp, deckBuf);
const deck = execFileSync('sh', ['-c', `unzip -p '${deckTmp.pathname}' 'ppt/slides/*.xml'`], { maxBuffer: 64e6 }).toString('utf8');
t('deck is a readable pptx with slides', deck.includes('<p:sld') && deck.length > 5000);
const docx2Buf = Buffer.from(await (await fetch(`${B}/api/assessments/${a.id}/docx`)).arrayBuffer());
writeFileSync(tmp, docx2Buf);
const docx2 = execFileSync('unzip', ['-p', tmp.pathname, 'word/document.xml'], { maxBuffer: 32e6 }).toString('utf8');
const safe2 = JSON.stringify(await json(`/api/assessments/${a.id}/report`));
for (const [name, text] of [['deck', deck], ['Word', docx2]]) {
t(`${name} omits our annual price`, !text.includes(fmt(PRICE)) && !text.includes('7.7M'));
t(`${name} omits our services figure`, !text.includes(fmt(SERVICES)));
t(`${name} never says "subscription"`, !/subscription/i.test(text));
t(`${name} never names an entitlement`, !/entitle/i.test(text));
}
t('projection drops entitlement flags', !safe2.includes('alreadyEntitled'));
execFileSync('rm', ['-f', tmp.pathname, deckTmp.pathname]);
} finally {
await fetch(`${B}/api/assessments/${a.id}`, { method: 'DELETE' });
}
console.log(`\n${pass} passed, ${fail} failed`);
process.exit(fail ? 1 : 0);