@vacbo/opencode-anthropic-fix 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -88
- package/dist/opencode-anthropic-auth-cli.mjs +804 -507
- package/dist/opencode-anthropic-auth-plugin.js +4751 -4109
- package/package.json +67 -59
- package/src/__tests__/billing-edge-cases.test.ts +59 -59
- package/src/__tests__/bun-proxy.parallel.test.ts +388 -382
- package/src/__tests__/cc-comparison.test.ts +87 -87
- package/src/__tests__/cc-credentials.test.ts +254 -250
- package/src/__tests__/cch-drift-checker.test.ts +51 -51
- package/src/__tests__/cch-native-style.test.ts +56 -56
- package/src/__tests__/debug-gating.test.ts +42 -42
- package/src/__tests__/decomposition-smoke.test.ts +68 -68
- package/src/__tests__/fingerprint-regression.test.ts +575 -566
- package/src/__tests__/helpers/conversation-history.smoke.test.ts +271 -271
- package/src/__tests__/helpers/conversation-history.ts +119 -119
- package/src/__tests__/helpers/deferred.smoke.test.ts +103 -103
- package/src/__tests__/helpers/deferred.ts +69 -69
- package/src/__tests__/helpers/in-memory-storage.smoke.test.ts +155 -155
- package/src/__tests__/helpers/in-memory-storage.ts +88 -88
- package/src/__tests__/helpers/mock-bun-proxy.smoke.test.ts +68 -68
- package/src/__tests__/helpers/mock-bun-proxy.ts +189 -189
- package/src/__tests__/helpers/plugin-fetch-harness.smoke.test.ts +273 -273
- package/src/__tests__/helpers/plugin-fetch-harness.ts +288 -288
- package/src/__tests__/helpers/sse.smoke.test.ts +236 -236
- package/src/__tests__/helpers/sse.ts +209 -209
- package/src/__tests__/index.parallel.test.ts +605 -595
- package/src/__tests__/sanitization-regex.test.ts +112 -112
- package/src/__tests__/state-bounds.test.ts +90 -90
- package/src/account-identity.test.ts +197 -192
- package/src/account-identity.ts +69 -67
- package/src/account-state.test.ts +86 -86
- package/src/account-state.ts +25 -25
- package/src/accounts/matching.test.ts +335 -0
- package/src/accounts/matching.ts +167 -0
- package/src/accounts/persistence.test.ts +345 -0
- package/src/accounts/persistence.ts +432 -0
- package/src/accounts/repair.test.ts +276 -0
- package/src/accounts/repair.ts +407 -0
- package/src/accounts.dedup.test.ts +621 -621
- package/src/accounts.test.ts +933 -929
- package/src/accounts.ts +633 -989
- package/src/backoff.test.ts +345 -345
- package/src/backoff.ts +219 -219
- package/src/betas.ts +124 -124
- package/src/bun-fetch.test.ts +345 -342
- package/src/bun-fetch.ts +424 -424
- package/src/bun-proxy.test.ts +25 -25
- package/src/bun-proxy.ts +209 -209
- package/src/cc-credentials.ts +111 -111
- package/src/circuit-breaker.test.ts +184 -184
- package/src/circuit-breaker.ts +169 -169
- package/src/cli/commands/auth.ts +963 -0
- package/src/cli/commands/config.ts +547 -0
- package/src/cli/formatting.test.ts +406 -0
- package/src/cli/formatting.ts +219 -0
- package/src/cli.ts +255 -2022
- package/src/commands/handlers/betas.ts +100 -0
- package/src/commands/handlers/config.ts +99 -0
- package/src/commands/handlers/files.ts +375 -0
- package/src/commands/oauth-flow.ts +181 -166
- package/src/commands/prompts.ts +61 -61
- package/src/commands/router.test.ts +421 -0
- package/src/commands/router.ts +143 -635
- package/src/config.test.ts +482 -482
- package/src/config.ts +412 -404
- package/src/constants.ts +48 -48
- package/src/drift/cch-constants.ts +95 -95
- package/src/env.ts +111 -105
- package/src/headers/billing.ts +33 -33
- package/src/headers/builder.ts +130 -130
- package/src/headers/cch.ts +75 -75
- package/src/headers/stainless.ts +25 -25
- package/src/headers/user-agent.ts +23 -23
- package/src/index.ts +436 -828
- package/src/models.ts +27 -27
- package/src/oauth.test.ts +102 -102
- package/src/oauth.ts +178 -178
- package/src/parent-pid-watcher.test.ts +148 -148
- package/src/parent-pid-watcher.ts +69 -69
- package/src/plugin-helpers.ts +82 -82
- package/src/refresh-helpers.ts +145 -139
- package/src/refresh-lock.test.ts +94 -94
- package/src/refresh-lock.ts +93 -93
- package/src/request/body.history.test.ts +579 -571
- package/src/request/body.ts +255 -255
- package/src/request/metadata.ts +65 -65
- package/src/request/retry.test.ts +156 -156
- package/src/request/retry.ts +67 -67
- package/src/request/url.ts +21 -21
- package/src/request-orchestration-helpers.ts +648 -0
- package/src/response/index.ts +5 -5
- package/src/response/mcp.ts +58 -58
- package/src/response/streaming.test.ts +313 -311
- package/src/response/streaming.ts +412 -410
- package/src/rotation.test.ts +304 -301
- package/src/rotation.ts +205 -205
- package/src/storage.test.ts +547 -547
- package/src/storage.ts +315 -291
- package/src/system-prompt/builder.ts +38 -38
- package/src/system-prompt/index.ts +5 -5
- package/src/system-prompt/normalize.ts +60 -60
- package/src/system-prompt/sanitize.ts +30 -30
- package/src/thinking.ts +21 -20
- package/src/token-refresh.test.ts +265 -265
- package/src/token-refresh.ts +219 -214
- package/src/types.ts +30 -30
- package/dist/bun-proxy.mjs +0 -291
|
@@ -6,35 +6,35 @@ import { createCircuitBreaker, CircuitState } from "./circuit-breaker.js";
|
|
|
6
6
|
// ---------------------------------------------------------------------------
|
|
7
7
|
|
|
8
8
|
describe("CircuitBreaker - State Management", () => {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
it("starts in CLOSED state and allows requests", () => {
|
|
10
|
+
const breaker = createCircuitBreaker({ failureThreshold: 3 });
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
expect(breaker.getState()).toBe(CircuitState.CLOSED);
|
|
13
|
+
expect(breaker.canExecute()).toBe(true);
|
|
14
|
+
});
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
it("transitions to OPEN after N consecutive failures", () => {
|
|
17
|
+
const breaker = createCircuitBreaker({ failureThreshold: 3 });
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
breaker.recordFailure();
|
|
20
|
+
breaker.recordFailure();
|
|
21
|
+
expect(breaker.getState()).toBe(CircuitState.CLOSED);
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
breaker.recordFailure();
|
|
24
|
+
expect(breaker.getState()).toBe(CircuitState.OPEN);
|
|
25
|
+
expect(breaker.canExecute()).toBe(false);
|
|
26
|
+
});
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
it("resets failure count on success", () => {
|
|
29
|
+
const breaker = createCircuitBreaker({ failureThreshold: 3 });
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
breaker.recordFailure();
|
|
32
|
+
breaker.recordFailure();
|
|
33
|
+
breaker.recordSuccess();
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
expect(breaker.getState()).toBe(CircuitState.CLOSED);
|
|
36
|
+
expect(breaker.getFailureCount()).toBe(0);
|
|
37
|
+
});
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
// ---------------------------------------------------------------------------
|
|
@@ -42,35 +42,35 @@ describe("CircuitBreaker - State Management", () => {
|
|
|
42
42
|
// ---------------------------------------------------------------------------
|
|
43
43
|
|
|
44
44
|
describe("CircuitBreaker - Open State", () => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
it("fails fast without calling upstream when OPEN", () => {
|
|
46
|
+
const breaker = createCircuitBreaker({
|
|
47
|
+
failureThreshold: 1,
|
|
48
|
+
resetTimeoutMs: 5000,
|
|
49
|
+
});
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
breaker.recordFailure();
|
|
52
|
+
expect(breaker.getState()).toBe(CircuitState.OPEN);
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
const upstreamCall = vi.fn();
|
|
55
|
+
const result = breaker.execute(upstreamCall);
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("tracks open state duration", () => {
|
|
63
|
-
const breaker = createCircuitBreaker({
|
|
64
|
-
failureThreshold: 1,
|
|
65
|
-
resetTimeoutMs: 5000,
|
|
57
|
+
expect(upstreamCall).not.toHaveBeenCalled();
|
|
58
|
+
expect(result.success).toBe(false);
|
|
59
|
+
expect(result.error).toBe("Circuit breaker is OPEN");
|
|
66
60
|
});
|
|
67
61
|
|
|
68
|
-
|
|
69
|
-
|
|
62
|
+
it("tracks open state duration", () => {
|
|
63
|
+
const breaker = createCircuitBreaker({
|
|
64
|
+
failureThreshold: 1,
|
|
65
|
+
resetTimeoutMs: 5000,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const openTime = Date.now();
|
|
69
|
+
breaker.recordFailure();
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
expect(breaker.getState()).toBe(CircuitState.OPEN);
|
|
72
|
+
expect(breaker.getOpenedAt()).toBeGreaterThanOrEqual(openTime);
|
|
73
|
+
});
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
// ---------------------------------------------------------------------------
|
|
@@ -78,50 +78,50 @@ describe("CircuitBreaker - Open State", () => {
|
|
|
78
78
|
// ---------------------------------------------------------------------------
|
|
79
79
|
|
|
80
80
|
describe("CircuitBreaker - Half-Open State", () => {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
it("transitions to HALF_OPEN after timeout expires", async () => {
|
|
82
|
+
const breaker = createCircuitBreaker({
|
|
83
|
+
failureThreshold: 1,
|
|
84
|
+
resetTimeoutMs: 100,
|
|
85
|
+
});
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
breaker.recordFailure();
|
|
88
|
+
expect(breaker.getState()).toBe(CircuitState.OPEN);
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("closes breaker on success in HALF_OPEN state", () => {
|
|
97
|
-
const breaker = createCircuitBreaker({
|
|
98
|
-
failureThreshold: 1,
|
|
99
|
-
resetTimeoutMs: 0,
|
|
92
|
+
expect(breaker.getState()).toBe(CircuitState.HALF_OPEN);
|
|
93
|
+
expect(breaker.canExecute()).toBe(true);
|
|
100
94
|
});
|
|
101
95
|
|
|
102
|
-
breaker
|
|
103
|
-
|
|
104
|
-
|
|
96
|
+
it("closes breaker on success in HALF_OPEN state", () => {
|
|
97
|
+
const breaker = createCircuitBreaker({
|
|
98
|
+
failureThreshold: 1,
|
|
99
|
+
resetTimeoutMs: 0,
|
|
100
|
+
});
|
|
105
101
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
});
|
|
102
|
+
breaker.recordFailure();
|
|
103
|
+
breaker.transitionToHalfOpen();
|
|
104
|
+
expect(breaker.getState()).toBe(CircuitState.HALF_OPEN);
|
|
110
105
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
resetTimeoutMs: 0,
|
|
106
|
+
breaker.recordSuccess();
|
|
107
|
+
expect(breaker.getState()).toBe(CircuitState.CLOSED);
|
|
108
|
+
expect(breaker.getFailureCount()).toBe(0);
|
|
115
109
|
});
|
|
116
110
|
|
|
117
|
-
breaker
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
111
|
+
it("reopens breaker on failure in HALF_OPEN state", () => {
|
|
112
|
+
const breaker = createCircuitBreaker({
|
|
113
|
+
failureThreshold: 3,
|
|
114
|
+
resetTimeoutMs: 0,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
breaker.recordFailure();
|
|
118
|
+
breaker.recordFailure();
|
|
119
|
+
breaker.transitionToHalfOpen();
|
|
120
|
+
expect(breaker.getState()).toBe(CircuitState.HALF_OPEN);
|
|
121
121
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
breaker.recordFailure();
|
|
123
|
+
expect(breaker.getState()).toBe(CircuitState.OPEN);
|
|
124
|
+
});
|
|
125
125
|
});
|
|
126
126
|
|
|
127
127
|
// ---------------------------------------------------------------------------
|
|
@@ -129,63 +129,63 @@ describe("CircuitBreaker - Half-Open State", () => {
|
|
|
129
129
|
// ---------------------------------------------------------------------------
|
|
130
130
|
|
|
131
131
|
describe("CircuitBreaker - Per-Client Isolation", () => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
132
|
+
it("client A failures do not affect client B breaker", () => {
|
|
133
|
+
const clientABreaker = createCircuitBreaker({
|
|
134
|
+
clientId: "client-a",
|
|
135
|
+
failureThreshold: 3,
|
|
136
|
+
});
|
|
137
|
+
const clientBBreaker = createCircuitBreaker({
|
|
138
|
+
clientId: "client-b",
|
|
139
|
+
failureThreshold: 3,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Client A fails 3 times
|
|
143
|
+
clientABreaker.recordFailure();
|
|
144
|
+
clientABreaker.recordFailure();
|
|
145
|
+
clientABreaker.recordFailure();
|
|
146
|
+
|
|
147
|
+
// Client A should be OPEN
|
|
148
|
+
expect(clientABreaker.getState()).toBe(CircuitState.OPEN);
|
|
149
|
+
expect(clientABreaker.canExecute()).toBe(false);
|
|
150
|
+
|
|
151
|
+
// Client B should still be CLOSED
|
|
152
|
+
expect(clientBBreaker.getState()).toBe(CircuitState.CLOSED);
|
|
153
|
+
expect(clientBBreaker.canExecute()).toBe(true);
|
|
140
154
|
});
|
|
141
155
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const clientABreaker = createCircuitBreaker({
|
|
158
|
-
clientId: "client-a",
|
|
159
|
-
failureThreshold: 3,
|
|
156
|
+
it("each client has independent failure count", () => {
|
|
157
|
+
const clientABreaker = createCircuitBreaker({
|
|
158
|
+
clientId: "client-a",
|
|
159
|
+
failureThreshold: 3,
|
|
160
|
+
});
|
|
161
|
+
const clientBBreaker = createCircuitBreaker({
|
|
162
|
+
clientId: "client-b",
|
|
163
|
+
failureThreshold: 3,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
clientABreaker.recordFailure();
|
|
167
|
+
clientABreaker.recordFailure();
|
|
168
|
+
|
|
169
|
+
expect(clientABreaker.getFailureCount()).toBe(2);
|
|
170
|
+
expect(clientBBreaker.getFailureCount()).toBe(0);
|
|
160
171
|
});
|
|
161
|
-
const clientBBreaker = createCircuitBreaker({
|
|
162
|
-
clientId: "client-b",
|
|
163
|
-
failureThreshold: 3,
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
clientABreaker.recordFailure();
|
|
167
|
-
clientABreaker.recordFailure();
|
|
168
172
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
173
|
+
it("shared registry returns same breaker for same clientId", () => {
|
|
174
|
+
const breaker1 = createCircuitBreaker({
|
|
175
|
+
clientId: "shared-client",
|
|
176
|
+
failureThreshold: 3,
|
|
177
|
+
});
|
|
178
|
+
const breaker2 = createCircuitBreaker({
|
|
179
|
+
clientId: "shared-client",
|
|
180
|
+
failureThreshold: 3,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
breaker1.recordFailure();
|
|
184
|
+
breaker1.recordFailure();
|
|
185
|
+
|
|
186
|
+
// Both references should see the same failure count
|
|
187
|
+
expect(breaker2.getFailureCount()).toBe(2);
|
|
181
188
|
});
|
|
182
|
-
|
|
183
|
-
breaker1.recordFailure();
|
|
184
|
-
breaker1.recordFailure();
|
|
185
|
-
|
|
186
|
-
// Both references should see the same failure count
|
|
187
|
-
expect(breaker2.getFailureCount()).toBe(2);
|
|
188
|
-
});
|
|
189
189
|
});
|
|
190
190
|
|
|
191
191
|
// ---------------------------------------------------------------------------
|
|
@@ -193,37 +193,37 @@ describe("CircuitBreaker - Per-Client Isolation", () => {
|
|
|
193
193
|
// ---------------------------------------------------------------------------
|
|
194
194
|
|
|
195
195
|
describe("CircuitBreaker - Configuration", () => {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
expect(breaker.getConfig().failureThreshold).toBe(5);
|
|
200
|
-
expect(breaker.getConfig().resetTimeoutMs).toBe(30000);
|
|
201
|
-
});
|
|
196
|
+
it("uses default values when options not provided", () => {
|
|
197
|
+
const breaker = createCircuitBreaker({});
|
|
202
198
|
|
|
203
|
-
|
|
204
|
-
|
|
199
|
+
expect(breaker.getConfig().failureThreshold).toBe(5);
|
|
200
|
+
expect(breaker.getConfig().resetTimeoutMs).toBe(30000);
|
|
201
|
+
});
|
|
205
202
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
expect(breaker.getState()).toBe(CircuitState.CLOSED);
|
|
203
|
+
it("respects custom failure threshold", () => {
|
|
204
|
+
const breaker = createCircuitBreaker({ failureThreshold: 10 });
|
|
210
205
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
206
|
+
for (let i = 0; i < 9; i++) {
|
|
207
|
+
breaker.recordFailure();
|
|
208
|
+
}
|
|
209
|
+
expect(breaker.getState()).toBe(CircuitState.CLOSED);
|
|
214
210
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
failureThreshold: 1,
|
|
218
|
-
resetTimeoutMs: 50,
|
|
211
|
+
breaker.recordFailure();
|
|
212
|
+
expect(breaker.getState()).toBe(CircuitState.OPEN);
|
|
219
213
|
});
|
|
220
214
|
|
|
221
|
-
|
|
222
|
-
|
|
215
|
+
it("respects custom reset timeout", async () => {
|
|
216
|
+
const breaker = createCircuitBreaker({
|
|
217
|
+
failureThreshold: 1,
|
|
218
|
+
resetTimeoutMs: 50,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
breaker.recordFailure();
|
|
222
|
+
expect(breaker.getState()).toBe(CircuitState.OPEN);
|
|
223
223
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
224
|
+
await new Promise((resolve) => setTimeout(resolve, 60));
|
|
225
|
+
expect(breaker.getState()).toBe(CircuitState.HALF_OPEN);
|
|
226
|
+
});
|
|
227
227
|
});
|
|
228
228
|
|
|
229
229
|
// ---------------------------------------------------------------------------
|
|
@@ -231,44 +231,44 @@ describe("CircuitBreaker - Configuration", () => {
|
|
|
231
231
|
// ---------------------------------------------------------------------------
|
|
232
232
|
|
|
233
233
|
describe("CircuitBreaker - Execute Wrapper", () => {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
234
|
+
it("executes upstream function when CLOSED", async () => {
|
|
235
|
+
const breaker = createCircuitBreaker({ failureThreshold: 3 });
|
|
236
|
+
const upstreamFn = vi.fn().mockResolvedValue("success");
|
|
237
237
|
|
|
238
|
-
|
|
238
|
+
const result = await breaker.execute(upstreamFn);
|
|
239
239
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
240
|
+
expect(upstreamFn).toHaveBeenCalledTimes(1);
|
|
241
|
+
expect(result.success).toBe(true);
|
|
242
|
+
expect(result.data).toBe("success");
|
|
243
|
+
});
|
|
244
244
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
245
|
+
it("records success when upstream succeeds", async () => {
|
|
246
|
+
const breaker = createCircuitBreaker({ failureThreshold: 3 });
|
|
247
|
+
const upstreamFn = vi.fn().mockResolvedValue("data");
|
|
248
248
|
|
|
249
|
-
|
|
249
|
+
await breaker.execute(upstreamFn);
|
|
250
250
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
251
|
+
expect(breaker.getFailureCount()).toBe(0);
|
|
252
|
+
expect(breaker.getState()).toBe(CircuitState.CLOSED);
|
|
253
|
+
});
|
|
254
254
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
255
|
+
it("records failure when upstream throws", async () => {
|
|
256
|
+
const breaker = createCircuitBreaker({ failureThreshold: 3 });
|
|
257
|
+
const upstreamFn = vi.fn().mockRejectedValue(new Error("upstream error"));
|
|
258
258
|
|
|
259
|
-
|
|
259
|
+
await breaker.execute(upstreamFn);
|
|
260
260
|
|
|
261
|
-
|
|
262
|
-
|
|
261
|
+
expect(breaker.getFailureCount()).toBe(1);
|
|
262
|
+
});
|
|
263
263
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
264
|
+
it("returns error result when upstream throws", async () => {
|
|
265
|
+
const breaker = createCircuitBreaker({ failureThreshold: 3 });
|
|
266
|
+
const error = new Error("upstream error");
|
|
267
|
+
const upstreamFn = vi.fn().mockRejectedValue(error);
|
|
268
268
|
|
|
269
|
-
|
|
269
|
+
const result = await breaker.execute(upstreamFn);
|
|
270
270
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
271
|
+
expect(result.success).toBe(false);
|
|
272
|
+
expect(result.error).toBe("upstream error");
|
|
273
|
+
});
|
|
274
274
|
});
|