import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import { buildProjectDossierRows } from '../../resources/js/lib/project-dossier.ts'; import { proactiveInvestmentRows } from '../../resources/js/lib/proactive-investment-report.ts'; const source = (id, need) => ({ key: 'opportunity:' + id, title: 'SYNTHETIC QA ' + id, version: 3, analysis: { proactive: { version: 1, group: 'ideas', state: 'hypothesis', need, proposal: 'Solução ' + id, basis: 'Fixture pública sintética', limitations: 'Não comprova procura real.', next_action: 'Confirmar dados reais.', evidence: [ { statement: 'QA facto ' + id, source: 'QA exemplar', locator: 'Fixture linha1', period: '2026', scope: 'Piloto', coverage: 'Sintética', verified_on: '2026-10-27', }, ], }, }, }); void test('absent proactivity is a no-op for accepted dossiers', () => { assert.deepEqual(proactiveInvestmentRows({}), []); assert.deepEqual( proactiveInvestmentRows({ context_applicability_snapshot: '{"version":1,"opportunity_choices":[]}', }), [], ); assert.deepEqual( proactiveInvestmentRows({ context_opportunity_snapshot: 'unreadable-original-preserved', }), [], ); }); void test('each idea retains origin,source,need,limits without summing or inventing economic values', () => { const choices = [ { opportunity: source(1, 'A') }, { opportunity: source(2, 'B') }, ], d = { context_applicability_snapshot: JSON.stringify({ version: 1, opportunity_choices: choices, }), }, r = proactiveInvestmentRows(d); assert.equal( r.filter((x) => x[1] === 'Origem da análise e ideia').length, 2, ); assert.match(JSON.stringify(r), /opportunity:1/); assert.match(JSON.stringify(r), /opportunity:2/); assert.match(JSON.stringify(r), /Hipótese analítica/); assert.match(JSON.stringify(r), /não é zero/); assert.equal( d.context_applicability_snapshot, JSON.stringify({ version: 1, opportunity_choices: choices }), ); }); void test('consumer selection only reports its idea even when other source contexts are present', () => { const choices = [ { opportunity: source(1, 'A') }, { opportunity: source(2, 'B') }, ], r = proactiveInvestmentRows({ context_opportunity: 'opportunity:2', context_applicability_snapshot: JSON.stringify({ version: 1, opportunity_choices: choices, }), }); assert.doesNotMatch(JSON.stringify(r), /opportunity:1/); assert.match(JSON.stringify(r), /opportunity:2/); }); void test('explicit consumer snapshot is retained and repeat references do not duplicate report rows', () => { const a = source(1, 'A'); let r = proactiveInvestmentRows({ context_opportunity_snapshot: JSON.stringify({ choice: { opportunity: a }, }), }); assert.equal( r.filter((x) => x[1] === 'Origem da análise e ideia').length, 1, ); r = proactiveInvestmentRows({ context_applicability_snapshot: JSON.stringify({ version: 1, opportunity_choices: [{ opportunity: a }, { opportunity: a }], }), }); assert.equal( r.filter((x) => x[1] === 'Origem da análise e ideia').length, 1, ); }); void test('portable API consumer snapshot produces an independently scoped report', () => { const d = JSON.parse( fs.readFileSync( new URL('./fixtures/proactive-api-consumers.json', import.meta.url), 'utf8', ), ); const before = JSON.stringify(d); const perConsumer = d.consumer_contexts.map((c) => proactiveInvestmentRows(c.prefill.simulator), ); for (const rows of perConsumer) assert.equal( rows.filter((x) => x[1] === 'Origem da análise e ideia').length, 1, ); const r = perConsumer.flat(); assert.equal( r.filter((x) => x[1] === 'Origem da análise e ideia').length, 2, ); assert.match(JSON.stringify(perConsumer[0]), /opportunity:399/); assert.doesNotMatch(JSON.stringify(perConsumer[0]), /opportunity:400/); assert.match(JSON.stringify(perConsumer[1]), /opportunity:400/); assert.doesNotMatch(JSON.stringify(perConsumer[1]), /opportunity:399/); assert.doesNotMatch(JSON.stringify(r), /PRIVATE-QA-/); assert.equal(JSON.stringify(d), before); }); void test('resources,assumptions and selected relationships are retained in the existing dossier report', () => { const a = source(7, 'A'); Object.assign(a.analysis.proactive, { resources: 'QA recurso declarado', assumptions: 'QA pressuposto explícito', conditions: 'QA condição', relations: [ { kind: 'project', target_id: 'QA-reference', label: 'QA projecto existente', relationship: 'hypothesis', basis: 'QA fundamento público', }, ], }); const draft = { context_applicability_snapshot: JSON.stringify({ version: 1, opportunity_choices: [{ opportunity: a }], }), }, rows = buildProjectDossierRows(draft); assert.match(JSON.stringify(rows), /QA pressuposto explícito/); assert.match(JSON.stringify(rows), /QA projecto existente/); assert.match( JSON.stringify(rows), /sem contrato, acesso ou participação presumidos/, ); }); void test('existing facts hide unrelated need and idea rows without deleting previous preparation', () => { const a = source(8, 'Old need'); a.analysis.proactive.group = 'exists'; a.analysis.proactive.proposal = 'Old idea'; const d = { context_applicability_snapshot: JSON.stringify({ version: 1, opportunity_choices: [{ opportunity: a }], }), }, before = d.context_applicability_snapshot, r = proactiveInvestmentRows(d); assert.equal( r.some( (x) => x[1] === 'Estado da necessidade' || x[1] === 'Proposta de investimento', ), false, ); assert.equal(d.context_applicability_snapshot, before); assert.match(JSON.stringify(r), /QA facto 8/); });