File size: 5,912 Bytes
9e4583c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import test from "node:test";
import assert from "node:assert/strict";

const { checkFallbackError, getProviderProfile } =
  await import("../../open-sse/services/accountFallback.ts");

const { getProviderCategory } = await import("../../open-sse/config/providerRegistry.ts");

const { COOLDOWN_MS, PROVIDER_PROFILES, RateLimitReason } =
  await import("../../open-sse/config/constants.ts");

// ─── Provider Category Tests ────────────────────────────────────────────────

test("getProviderCategory: OAuth providers return 'oauth'", () => {
  assert.equal(getProviderCategory("claude"), "oauth");
  assert.equal(getProviderCategory("codex"), "oauth");
  assert.equal(getProviderCategory("github"), "oauth");
  assert.equal(getProviderCategory("antigravity"), "oauth");
  assert.equal(getProviderCategory("cursor"), "oauth");
  assert.equal(getProviderCategory("kiro"), "oauth");
  assert.equal(getProviderCategory("gemini-cli"), "oauth");
  assert.equal(getProviderCategory("cline"), "oauth");
});

test("getProviderCategory: API key providers return 'apikey'", () => {
  assert.equal(getProviderCategory("groq"), "apikey");
  assert.equal(getProviderCategory("fireworks"), "apikey");
  assert.equal(getProviderCategory("cerebras"), "apikey");
  assert.equal(getProviderCategory("nvidia"), "apikey");
  assert.equal(getProviderCategory("openai"), "apikey");
  assert.equal(getProviderCategory("anthropic"), "apikey");
  assert.equal(getProviderCategory("deepseek"), "apikey");
  assert.equal(getProviderCategory("gemini"), "apikey");
});

test("getProviderCategory: unknown provider defaults to 'apikey'", () => {
  assert.equal(getProviderCategory("nonexistent"), "apikey");
});

// ─── Provider Profile Tests ─────────────────────────────────────────────────

test("getProviderProfile: OAuth provider returns oauth profile", () => {
  const profile = getProviderProfile("claude");
  assert.deepEqual(profile, PROVIDER_PROFILES.oauth);
});

test("getProviderProfile: API provider returns apikey profile", () => {
  const profile = getProviderProfile("groq");
  assert.deepEqual(profile, PROVIDER_PROFILES.apikey);
});

test("getProviderProfile: profiles have different thresholds", () => {
  const oauth = getProviderProfile("claude");
  const api = getProviderProfile("groq");
  assert.ok(
    oauth.circuitBreakerThreshold < api.circuitBreakerThreshold,
    "OAuth should have lower threshold than API"
  );
  assert.ok(
    oauth.maxBackoffLevel > api.maxBackoffLevel,
    "OAuth should have higher max backoff level"
  );
});

// ─── Exponential Backoff for Transient Errors ───────────────────────────────

test("502 transient: exponential backoff 5s β†’ 10s β†’ 20s β†’ 40s β†’ 60s (capped)", () => {
  const cooldowns = [];
  for (let level = 0; level < 6; level++) {
    const result = checkFallbackError(502, "", level, null, null);
    cooldowns.push(result.cooldownMs);
    assert.equal(result.shouldFallback, true);
    assert.equal(result.newBackoffLevel, level + 1);
    assert.equal(result.reason, RateLimitReason.SERVER_ERROR);
  }
  // Without provider: uses COOLDOWN_MS.transientInitial (5s) as base
  assert.equal(cooldowns[0], COOLDOWN_MS.transientInitial); // 5s
  assert.equal(cooldowns[1], COOLDOWN_MS.transientInitial * 2); // 10s
  assert.equal(cooldowns[2], COOLDOWN_MS.transientInitial * 4); // 20s
  assert.equal(cooldowns[3], COOLDOWN_MS.transientInitial * 8); // 40s
  // Level 4: 5s * 16 = 80s β†’ capped at 60s
  assert.equal(cooldowns[4], COOLDOWN_MS.transientMax); // 60s
  assert.equal(cooldowns[5], COOLDOWN_MS.transientMax); // 60s (stays capped)
});

test("502 with OAuth provider: uses oauth profile transientCooldown", () => {
  const result = checkFallbackError(502, "", 0, null, "claude");
  assert.equal(result.cooldownMs, PROVIDER_PROFILES.oauth.transientCooldown); // 5s
  assert.equal(result.newBackoffLevel, 1);
});

test("502 with API provider: uses apikey profile transientCooldown", () => {
  const result = checkFallbackError(502, "", 0, null, "groq");
  assert.equal(result.cooldownMs, PROVIDER_PROFILES.apikey.transientCooldown); // 3s
  assert.equal(result.newBackoffLevel, 1);
});

test("502 with API provider: backoff respects apikey maxBackoffLevel", () => {
  const maxLevel = PROVIDER_PROFILES.apikey.maxBackoffLevel;
  const result = checkFallbackError(502, "", maxLevel, null, "groq");
  assert.equal(result.newBackoffLevel, maxLevel); // Capped
});

test("502 with OAuth provider: backoff respects oauth maxBackoffLevel", () => {
  const maxLevel = PROVIDER_PROFILES.oauth.maxBackoffLevel;
  const result = checkFallbackError(502, "", maxLevel, null, "claude");
  assert.equal(result.newBackoffLevel, maxLevel); // Capped
});

// ─── Other Error Types Still Work ───────────────────────────────────────────

test("429 rate limit: still uses quota-based exponential backoff", () => {
  const result = checkFallbackError(429, "", 0, null, "groq");
  assert.equal(result.shouldFallback, true);
  assert.equal(result.newBackoffLevel, 1);
  assert.equal(result.reason, RateLimitReason.RATE_LIMIT_EXCEEDED);
});

test("401 auth error: still uses flat cooldown, no backoff", () => {
  const result = checkFallbackError(401, "", 0, null, "groq");
  assert.equal(result.shouldFallback, true);
  assert.equal(result.cooldownMs, COOLDOWN_MS.unauthorized);
  assert.equal(result.newBackoffLevel, undefined);
});

test("400 bad request: still returns shouldFallback false", () => {
  const result = checkFallbackError(400, "", 0, null, "groq");
  assert.equal(result.shouldFallback, false);
});