Initial commit: Converge portfolio rationalization app
Next.js app for mapping a customer's competitive tool landscape onto our portfolio and building the consolidation business case: assessment workbench, customer report and Word export, product catalog, competitive knowledge base, dossier import, shareable landscape questionnaires with a review inbox, tool-footprint and business-unit value analysis, and an implementation timeline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
39
scripts/test-kb-pruning.mjs
Normal file
39
scripts/test-kb-pruning.mjs
Normal file
@@ -0,0 +1,39 @@
|
||||
const B='http://127.0.0.1:3500';
|
||||
const get=(u)=>fetch(B+u).then(r=>r.json());
|
||||
const put=(u,b)=>fetch(B+u,{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(b)}).then(r=>r.json());
|
||||
const del=(u)=>fetch(B+u,{method:'DELETE'}).then(r=>r.json());
|
||||
const post=(u,b)=>fetch(B+u,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(b)}).then(r=>r.json());
|
||||
let pass=0,fail=0; const t=(n,ok)=>{ok?(pass++,console.log(' PASS',n)):(fail++,console.log(' FAIL',n))};
|
||||
const entry=(kb,l)=>kb.entries.find(e=>e.label===l);
|
||||
const cand=(o)=>({id:'c-x',incumbentVendor:'',incumbentProduct:'',domain:'',productIds:[],annualSpend:0,units:0,unitLabel:'',renewalDate:'',capability:4,switchEase:3,timeToRoiMonths:12,savingsPct:50,migrationCost:0,strategicValue:3,risks:'',notes:'',included:true,...o});
|
||||
|
||||
// Self-contained: create an assessment, learn from it, then take things away.
|
||||
const a = await post('/api/assessments', { customerName: 'Prune Test Co' });
|
||||
a.candidates = [
|
||||
cand({ id:'c-1', incumbentVendor:'CyberArk', incumbentProduct:'PAM', domain:'iam', productIds:['esg-pam'], annualSpend:400000, savingsPct:55 }),
|
||||
cand({ id:'c-2', incumbentVendor:'Okta', incumbentProduct:'Workforce Identity', domain:'iam', productIds:['esg-siteminder'], annualSpend:300000, savingsPct:40 }),
|
||||
];
|
||||
await put('/api/assessments/'+a.id, a);
|
||||
|
||||
const learned = await get('/api/knowledge-base');
|
||||
t('both candidates learned', entry(learned,'CyberArk PAM').observations.some(o=>o.assessmentId===a.id)
|
||||
&& entry(learned,'Okta Workforce Identity').observations.some(o=>o.assessmentId===a.id));
|
||||
t('learned the real savings figure', entry(learned,'CyberArk PAM').savingsPct === 55);
|
||||
|
||||
// Remove one candidate -> only its observation is pruned.
|
||||
a.candidates = a.candidates.filter(c => c.id !== 'c-1');
|
||||
await put('/api/assessments/'+a.id, a);
|
||||
const pruned = await get('/api/knowledge-base');
|
||||
t('removed candidate is forgotten', !entry(pruned,'CyberArk PAM').observations.some(o=>o.assessmentId===a.id));
|
||||
t('defaults revert to seeded values', entry(pruned,'CyberArk PAM').savingsPct === 50);
|
||||
t('the entry survives for reuse', entry(pruned,'CyberArk PAM').productIds.includes('esg-pam'));
|
||||
t('the other candidate is untouched', entry(pruned,'Okta Workforce Identity').observations.some(o=>o.assessmentId===a.id));
|
||||
|
||||
// Delete the assessment -> everything it taught is forgotten.
|
||||
await del('/api/assessments/'+a.id);
|
||||
const gone = await get('/api/knowledge-base');
|
||||
t('deleting the assessment forgets the rest', !entry(gone,'Okta Workforce Identity').observations.some(o=>o.assessmentId===a.id));
|
||||
t('unrelated learning is preserved', entry(gone,'Splunk Enterprise').observations.length === 1);
|
||||
|
||||
console.log(`\n${pass} passed, ${fail} failed`);
|
||||
process.exit(fail?1:0);
|
||||
Reference in New Issue
Block a user