// Monotonie en fonction du budget (SPEC menu branches fiches §12, v0.1.1) : sur // les trois profils de référence et les trois axes, de 1 000 à 40 000 € par pas // de 500 €, le statut ne repasse jamais de « correct » à « budget insuffisant », // et le sous-total ne décroît pas au-delà de la tolérance de dépassement // (`budget_overrun_pct`, 5 % du budget). La stricte croissance n'est pas tenue // par l'heuristique (l'étape 3 choisit poste par poste, par paliers ; constaté // −4,4 % au plus sur A/énergie, voir RECAP-LOT-v0.1.1) : la borne retenue est // la bande de tolérance, celle que l'habitant voit déjà comme « correct ». import { test } from 'node:test' import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import { eau, energie, alimentation, contexte as moteurContexte } from '../dist/index.js' const lire = (f) => JSON.parse(readFileSync(new URL(`../fixtures/${f}`, import.meta.url), 'utf8')) const options = lire('options.json') const postes = lire('postes.json') const france_climat = lire('france_climat.json') const algo_config = lire('algo-config.json') const profils = lire('profils-reference.json') const donnees = { postes, options, algo_config, france_climat } const MOTEURS = { eau, energie, alim: alimentation } const tol = (algo_config.tolerances.budget_overrun_pct || 5) / 100 const BUDGETS = [] for (let b = 1000; b <= 40000; b += 500) BUDGETS.push(b) for (const profil of profils) { const ctx = moteurContexte.calculer({ code_postal: profil.inputs.code_postal }, { france_climat }).sorties for (const [axe, moteur] of Object.entries(MOTEURS)) { test(`${profil.nom} / ${axe} : statut jamais OK → INSUFFISANT, sous-total jamais en baisse au-delà de la tolérance (1 000 → 40 000 € par 500)`, () => { const serie = BUDGETS.map((b) => { const s = moteur.calculer({ autodiag: profil.inputs, budget_axe: b, contexte: ctx }, donnees).sorties assert.equal(s.sous_total_eur_ht, s.postes.reduce((a, p) => a + p.cout_eur_ht, 0), `${b} : sous-total = somme des lignes`) return { b, status: s.status, st: s.sous_total_eur_ht } }) for (let i = 1; i < serie.length; i++) { const p = serie[i - 1] const c = serie[i] assert.ok(!(p.status !== 'BUDGET_INSUFFISANT' && c.status === 'BUDGET_INSUFFISANT'), `statut ${p.status} à ${p.b} € puis BUDGET_INSUFFISANT à ${c.b} €`) assert.ok(c.st >= p.st - c.b * tol, `sous-total ${p.st} € à ${p.b} € puis ${c.st} € à ${c.b} € (baisse > ${tol * 100} % du budget)`) } }) } }