import test from 'node:test'; import assert from 'node:assert'; import { safeImport } from '../_helpers/path.js'; import { FakeLLMClient } from '../_helpers/fakeLLMClient.js'; function skip(msg){ console.warn('[SKIP]', msg); } test('LLMManager: circuit breaker opens after consecutive failures (optional)', async () => { const res = await safeImport('LLMManager'); if (!res.ok) { skip(res.reason); return; } const L = res.mod; if (typeof L.__setClient !== 'function' || typeof L.__setRetryPolicy !== 'function') { skip('Missing __setClient/__setRetryPolicy hooks'); return; } // Simule toujours 500 const fakeFail = new FakeLLMClient({ plan: [{err:{statusCode:500}}] }); fakeFail.invoke = async () => { throw Object.assign(new Error('500'), {statusCode:500}); }; L.__setClient('mock', fakeFail); L.__setRetryPolicy({ retries: 0, baseMs: 1, isTransient: (code) => [500].includes(code), openAfter: 3, // <-- si ton impl gère ce champ cooldownMs: 100 // <-- idem }); let fails = 0; for (let i=0; i<3; i++) { try { await L.callModel({ provider:'mock', model:'fake-1', input:`try-${i}` }); } catch(e){ fails++; } } assert.equal(fails, 3); // Après 3, le breaker devrait s'ouvrir (si implémenté) let breakerThrew = false; try { await L.callModel({ provider:'mock', model:'fake-1', input:'blocked' }); } catch(e){ breakerThrew = true; } // On accepte SKIP si non implémenté dans ta lib if (!breakerThrew) skip('Circuit breaker not implemented; test informational only'); });