seo-generator-server/tests/llm/llmmanager.circuitbreaker.test.js
2025-09-03 15:29:19 +08:00

44 lines
1.5 KiB
JavaScript

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');
});