195 lines
6.7 KiB
JavaScript
195 lines
6.7 KiB
JavaScript
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/);
|
|
});
|