From a2b89489a69c28d3436f2ecf5ccd7ef9c42a005e Mon Sep 17 00:00:00 2001 From: Jules Neny Date: Tue, 29 Sep 2026 00:27:01 +0200 Subject: [PATCH] =?UTF-8?q?fix(budget):=20une=20table=20sans=20aucune=20li?= =?UTF-8?q?gne=20dat=C3=A9e=20n'est=20jamais=20=C2=AB=200=20=E2=82=AC=20v?= =?UTF-8?q?=C3=A9rifi=C3=A9=20=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Passe /autofeedback (angle checkpoint) : si NocoDB renvoie des lignes sans timestamp, CreatedAt ni created_at, checkBudget rendait verified=true à 0 €, soit le fail-open silencieux d'avant sous une autre forme. Désormais verified=false et avertissement au journal. 17/17. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0174RrDEQFQTtySXkTcsUKuv --- scripts/test-circuit-breaker.mjs | 10 ++++++++++ server/utils/circuitBreaker.ts | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/scripts/test-circuit-breaker.mjs b/scripts/test-circuit-breaker.mjs index 81b5d63..cf14f57 100644 --- a/scripts/test-circuit-breaker.mjs +++ b/scripts/test-circuit-breaker.mjs @@ -128,6 +128,16 @@ try { check('journal : lignes sans date signalées', logs.some(([, t]) => t.includes('1 sans date ignorées'))) } + // 3bis. Aucune ligne datée : jamais « 0 € vérifié » + { + const rows = [{ Id: 1, cout_eur: 30 }, { Id: 2, cout_eur: 1 }] + const { fetchJson } = fauxNocodb({ schema: ['Id', 'cout_eur'], rows }) + logs.length = 0 + const s = await checkBudget({ ...base, fetchJson }) + check('aucune date : verified=false, pas 0 € vérifié', s.verified === false && s.lignesSansDate === 2) + check('aucune date : avertissement « NON vérifié »', logs.some(([niv, t]) => niv === 'warn' && t.includes('NON vérifié'))) + } + // 4. Pagination : 2 500 lignes du mois à 0,01 € → 25 € → bloqué { const rows = Array.from({ length: 2500 }, (_, i) => ({ Id: i + 1, cout_eur: 0.01, CreatedAt: '2026-09-20T00:00:00Z' })) diff --git a/server/utils/circuitBreaker.ts b/server/utils/circuitBreaker.ts index b0454b9..73262d3 100644 --- a/server/utils/circuitBreaker.ts +++ b/server/utils/circuitBreaker.ts @@ -113,6 +113,15 @@ export async function checkBudget(config: { return { cumulEur: 0, blocked: false, warning: false, verified: false, lignesLues, lignesMois, lignesSansDate } } + // Des lignes, mais aucune datée : on ne sait pas ce qui relève du mois. + // Rendre « 0 € vérifié » reproduirait le fail-open silencieux d'avant AF5. + if (lignesLues > 0 && lignesSansDate === lignesLues) { + console.warn( + `[circuitBreaker] budget NON vérifié : ${lignesLues} lignes stats_usage sans timestamp, CreatedAt ni created_at`, + ) + return { cumulEur: 0, blocked: false, warning: false, verified: false, lignesLues, lignesMois, lignesSansDate } + } + const status: BudgetStatus = { cumulEur, blocked: cumulEur >= BUDGET_MAX_EUR,