import test from "node:test"; import assert from "node:assert/strict"; // ─── Unit tests for anti-thundering herd logic ────────────────────────────── // These tests verify the accountFallback layer (provider profiles + backoff). // The auth.js mutex is an integration-level concern (DB-dependent) and is covered // by its own manual verification. const { checkFallbackError, getProviderProfile } = await import("../../open-sse/services/accountFallback.ts"); const { BACKOFF_CONFIG, PROVIDER_PROFILES, DEFAULT_API_LIMITS, COOLDOWN_MS, RateLimitReason } = await import("../../open-sse/config/constants.ts"); const { getProviderCategory } = await import("../../open-sse/config/providerRegistry.ts"); // ─── OAuth vs API Profile Differentiation ─────────────────────────────────── test("OAuth profile has stricter circuit breaker (lower threshold)", () => { const oauth = PROVIDER_PROFILES.oauth; const apikey = PROVIDER_PROFILES.apikey; assert.ok( oauth.circuitBreakerThreshold < apikey.circuitBreakerThreshold, "OAuth should open circuit faster" ); }); test("API profile has shorter transient cooldown", () => { const apikey = PROVIDER_PROFILES.apikey; const oauth = PROVIDER_PROFILES.oauth; assert.ok( apikey.transientCooldown < oauth.transientCooldown, "API providers should have shorter base cooldown" ); }); // ─── Backoff Ceiling Tests (prevents infinite growth) ─────────────────────── test("Exponential backoff clamps to the configured maxBackoffLevel", () => { const result = checkFallbackError(502, "", 20, null, null); assert.equal(result.newBackoffLevel, BACKOFF_CONFIG.maxLevel); assert.equal( result.cooldownMs, COOLDOWN_MS.transientInitial * Math.pow(2, BACKOFF_CONFIG.maxLevel) ); }); test("API provider backoff level caps at profile maxBackoffLevel", () => { const maxLevel = PROVIDER_PROFILES.apikey.maxBackoffLevel; const result = checkFallbackError(502, "", maxLevel, null, "groq"); assert.equal(result.newBackoffLevel, maxLevel, "Should not exceed max level"); }); test("OAuth provider backoff level caps at profile maxBackoffLevel", () => { const maxLevel = PROVIDER_PROFILES.oauth.maxBackoffLevel; const result = checkFallbackError(502, "", maxLevel, null, "claude"); assert.equal(result.newBackoffLevel, maxLevel, "Should not exceed max level"); }); // ─── Thundering Herd Simulation (unit level) ──────────────────────────────── // This tests that 5 concurrent 502 calls to checkFallbackError with the same // backoff level produce CONSISTENT results (same cooldown, same new level). test("5 concurrent 502 calls at same backoff level produce identical results", () => { const results = []; for (let i = 0; i < 5; i++) { results.push(checkFallbackError(502, "Bad Gateway", 0, null, "groq")); } // All should have same cooldown and new backoff level const first = results[0]; for (const r of results) { assert.equal(r.cooldownMs, first.cooldownMs, "Cooldowns should be identical"); assert.equal(r.newBackoffLevel, first.newBackoffLevel, "Backoff levels should be identical"); assert.equal(r.reason, RateLimitReason.SERVER_ERROR); } }); test("Progressive backoff: Level 0→1→2 produce increasing cooldowns", () => { const r0 = checkFallbackError(502, "", 0, null, "groq"); const r1 = checkFallbackError(502, "", 1, null, "groq"); const r2 = checkFallbackError(502, "", 2, null, "groq"); assert.ok(r1.cooldownMs > r0.cooldownMs, "Level 1 should be longer than level 0"); assert.ok(r2.cooldownMs > r1.cooldownMs, "Level 2 should be longer than level 1"); assert.equal(r0.newBackoffLevel, 1); assert.equal(r1.newBackoffLevel, 2); assert.equal(r2.newBackoffLevel, 3); }); // ─── DEFAULT_API_LIMITS Configuration ─────────────────────────────────────── test("DEFAULT_API_LIMITS has reasonable values", () => { assert.ok(DEFAULT_API_LIMITS.requestsPerMinute >= 60, "RPM should be at least 60"); assert.ok(DEFAULT_API_LIMITS.minTimeBetweenRequests >= 100, "minTime should be at least 100ms"); assert.ok(DEFAULT_API_LIMITS.concurrentRequests >= 5, "maxConcurrent should be at least 5"); }); test("API providers auto-enrolled are grouped correctly", () => { // Spot check: these should all be apikey const apiProviders = ["groq", "fireworks", "cerebras", "openai", "anthropic"]; const oauthProviders = ["claude", "codex", "github", "antigravity"]; for (const p of apiProviders) { assert.equal(getProviderCategory(p), "apikey", `${p} should be apikey`); } for (const p of oauthProviders) { assert.equal(getProviderCategory(p), "oauth", `${p} should be oauth`); } });