offrouter-core 0.2.1 → 0.2.2
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/dist/audit.d.ts +137 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +320 -0
- package/dist/audit.js.map +1 -0
- package/dist/auth/credential-chain.d.ts +84 -0
- package/dist/auth/credential-chain.d.ts.map +1 -0
- package/dist/auth/credential-chain.js +150 -0
- package/dist/auth/credential-chain.js.map +1 -0
- package/dist/auth/index.d.ts +10 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +7 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/keychain.d.ts +60 -0
- package/dist/auth/keychain.d.ts.map +1 -0
- package/dist/auth/keychain.js +104 -0
- package/dist/auth/keychain.js.map +1 -0
- package/dist/auth/oauth-pkce.d.ts +138 -0
- package/dist/auth/oauth-pkce.d.ts.map +1 -0
- package/dist/auth/oauth-pkce.js +375 -0
- package/dist/auth/oauth-pkce.js.map +1 -0
- package/dist/auth/oauth-server.d.ts +36 -0
- package/dist/auth/oauth-server.d.ts.map +1 -0
- package/dist/auth/oauth-server.js +210 -0
- package/dist/auth/oauth-server.js.map +1 -0
- package/dist/config.d.ts +76 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +355 -0
- package/dist/config.js.map +1 -0
- package/dist/delegation.d.ts +123 -0
- package/dist/delegation.d.ts.map +1 -0
- package/dist/delegation.js +455 -0
- package/dist/delegation.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/policy.d.ts +45 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +318 -0
- package/dist/policy.js.map +1 -0
- package/{src/protocol.ts → dist/protocol.d.ts} +2 -2
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +6 -0
- package/dist/protocol.js.map +1 -0
- package/dist/providers/adapter.d.ts +137 -0
- package/dist/providers/adapter.d.ts.map +1 -0
- package/dist/providers/adapter.js +163 -0
- package/dist/providers/adapter.js.map +1 -0
- package/dist/providers/catalog-data.d.ts +128 -0
- package/dist/providers/catalog-data.d.ts.map +1 -0
- package/dist/providers/catalog-data.js +397 -0
- package/dist/providers/catalog-data.js.map +1 -0
- package/dist/providers/catalog.d.ts +63 -0
- package/dist/providers/catalog.d.ts.map +1 -0
- package/dist/providers/catalog.js +394 -0
- package/dist/providers/catalog.js.map +1 -0
- package/dist/providers/fake.d.ts +46 -0
- package/dist/providers/fake.d.ts.map +1 -0
- package/dist/providers/fake.js +234 -0
- package/dist/providers/fake.js.map +1 -0
- package/dist/providers/openai-compatible.d.ts +65 -0
- package/dist/providers/openai-compatible.d.ts.map +1 -0
- package/dist/providers/openai-compatible.js +434 -0
- package/dist/providers/openai-compatible.js.map +1 -0
- package/dist/proxy/responses-mapper.d.ts +366 -0
- package/dist/proxy/responses-mapper.d.ts.map +1 -0
- package/dist/proxy/responses-mapper.js +517 -0
- package/dist/proxy/responses-mapper.js.map +1 -0
- package/dist/proxy/responses-server.d.ts +29 -0
- package/dist/proxy/responses-server.d.ts.map +1 -0
- package/dist/proxy/responses-server.js +360 -0
- package/dist/proxy/responses-server.js.map +1 -0
- package/dist/router.d.ts +18 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +296 -0
- package/dist/router.js.map +1 -0
- package/dist/schemas.d.ts +560 -0
- package/dist/schemas.d.ts.map +1 -0
- package/{src/schemas.ts → dist/schemas.js} +83 -103
- package/dist/schemas.js.map +1 -0
- package/dist/secrets.d.ts +112 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +326 -0
- package/dist/secrets.js.map +1 -0
- package/dist/types.d.ts +117 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/usage-file.d.ts +59 -0
- package/dist/usage-file.d.ts.map +1 -0
- package/dist/usage-file.js +316 -0
- package/dist/usage-file.js.map +1 -0
- package/dist/usage.d.ts +235 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +517 -0
- package/dist/usage.js.map +1 -0
- package/package.json +7 -2
- package/src/audit.test.ts +0 -302
- package/src/audit.ts +0 -553
- package/src/auth/credential-chain.test.ts +0 -326
- package/src/auth/credential-chain.ts +0 -235
- package/src/auth/index.ts +0 -45
- package/src/auth/keychain.test.ts +0 -265
- package/src/auth/keychain.ts +0 -168
- package/src/auth/oauth-pkce.test.ts +0 -329
- package/src/auth/oauth-pkce.ts +0 -571
- package/src/auth/oauth-server.test.ts +0 -83
- package/src/auth/oauth-server.ts +0 -296
- package/src/config.test.ts +0 -479
- package/src/config.ts +0 -505
- package/src/delegation.test.ts +0 -368
- package/src/delegation.ts +0 -605
- package/src/index.ts +0 -280
- package/src/policy.test.ts +0 -634
- package/src/policy.ts +0 -420
- package/src/providers/adapter.test.ts +0 -293
- package/src/providers/adapter.ts +0 -328
- package/src/providers/catalog-data.test.ts +0 -258
- package/src/providers/catalog-data.ts +0 -498
- package/src/providers/catalog.test.ts +0 -84
- package/src/providers/catalog.ts +0 -483
- package/src/providers/fake.ts +0 -312
- package/src/providers/openai-compatible.test.ts +0 -366
- package/src/providers/openai-compatible.ts +0 -554
- package/src/proxy/responses-mapper.test.ts +0 -290
- package/src/proxy/responses-mapper.ts +0 -736
- package/src/proxy/responses-server.test.ts +0 -322
- package/src/proxy/responses-server.ts +0 -469
- package/src/router.test.ts +0 -699
- package/src/router.ts +0 -352
- package/src/schemas.test.ts +0 -291
- package/src/secrets.test.ts +0 -271
- package/src/secrets.ts +0 -461
- package/src/types.ts +0 -173
- package/src/usage-file.test.ts +0 -243
- package/src/usage-file.ts +0 -435
- package/src/usage.test.ts +0 -335
- package/src/usage.ts +0 -859
- package/tsconfig.json +0 -9
package/src/usage.test.ts
DELETED
|
@@ -1,335 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
InMemoryStateStore,
|
|
4
|
-
InMemoryUsageStore,
|
|
5
|
-
type UsageEvent,
|
|
6
|
-
} from "./usage.js";
|
|
7
|
-
|
|
8
|
-
function err(
|
|
9
|
-
providerId: string,
|
|
10
|
-
kind: UsageEvent["kind"] = "request_error",
|
|
11
|
-
at?: Date,
|
|
12
|
-
): UsageEvent {
|
|
13
|
-
return {
|
|
14
|
-
providerId,
|
|
15
|
-
authTier: "subscription",
|
|
16
|
-
kind,
|
|
17
|
-
at,
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
describe("InMemoryUsageStore", () => {
|
|
22
|
-
it("applies concurrent usage updates without losing counters", async () => {
|
|
23
|
-
const store = new InMemoryUsageStore();
|
|
24
|
-
const providerId = "claude";
|
|
25
|
-
|
|
26
|
-
await Promise.all(
|
|
27
|
-
Array.from({ length: 50 }, () =>
|
|
28
|
-
store.record({
|
|
29
|
-
providerId,
|
|
30
|
-
authTier: "subscription",
|
|
31
|
-
kind: "subscription_usage",
|
|
32
|
-
amount: 1,
|
|
33
|
-
}),
|
|
34
|
-
),
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
const snap = await store.getProvider(providerId);
|
|
38
|
-
expect(snap?.subscriptionQuotaUsed).toBe(50);
|
|
39
|
-
expect(snap?.successCount).toBe(50);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("tracks quota remaining for subscription capacity", async () => {
|
|
43
|
-
const store = new InMemoryUsageStore();
|
|
44
|
-
await store.setSubscriptionQuota("claude", {
|
|
45
|
-
limit: 100,
|
|
46
|
-
used: 40,
|
|
47
|
-
status: "active",
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
let snap = await store.getProvider("claude");
|
|
51
|
-
expect(snap?.subscriptionQuotaLimit).toBe(100);
|
|
52
|
-
expect(snap?.subscriptionQuotaUsed).toBe(40);
|
|
53
|
-
expect(snap?.subscriptionQuotaRemaining).toBe(60);
|
|
54
|
-
expect(snap?.subscriptionStatus).toBe("active");
|
|
55
|
-
expect(snap?.health).toBe("healthy");
|
|
56
|
-
|
|
57
|
-
await store.record({
|
|
58
|
-
providerId: "claude",
|
|
59
|
-
authTier: "subscription",
|
|
60
|
-
kind: "subscription_usage",
|
|
61
|
-
amount: 25,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
snap = await store.getProvider("claude");
|
|
65
|
-
expect(snap?.subscriptionQuotaUsed).toBe(65);
|
|
66
|
-
expect(snap?.subscriptionQuotaRemaining).toBe(35);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("downgrades provider health via sliding-window error budget and cooldown", async () => {
|
|
70
|
-
const t0 = Date.UTC(2026, 0, 1, 12, 0, 0);
|
|
71
|
-
let nowMs = t0;
|
|
72
|
-
const store = new InMemoryUsageStore({
|
|
73
|
-
windowMs: 10_000,
|
|
74
|
-
healthyMaxErrors: 2,
|
|
75
|
-
deadMinErrors: 5,
|
|
76
|
-
cooldownMs: 30_000,
|
|
77
|
-
now: () => new Date(nowMs),
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// 0-2 errors remain healthy
|
|
81
|
-
await store.record(err("openai", "request_error", new Date(nowMs)));
|
|
82
|
-
await store.record(
|
|
83
|
-
err("openai", "request_error", new Date((nowMs += 10))),
|
|
84
|
-
);
|
|
85
|
-
let snap = await store.getProvider("openai");
|
|
86
|
-
expect(snap?.health).toBe("healthy");
|
|
87
|
-
expect(snap?.recentErrorCount).toBe(2);
|
|
88
|
-
|
|
89
|
-
// 3-4 => degraded
|
|
90
|
-
await store.record(
|
|
91
|
-
err("openai", "request_error", new Date((nowMs += 10))),
|
|
92
|
-
);
|
|
93
|
-
snap = await store.getProvider("openai");
|
|
94
|
-
expect(snap?.health).toBe("degraded");
|
|
95
|
-
|
|
96
|
-
await store.record(
|
|
97
|
-
err("openai", "request_error", new Date((nowMs += 10))),
|
|
98
|
-
);
|
|
99
|
-
snap = await store.getProvider("openai");
|
|
100
|
-
expect(snap?.health).toBe("degraded");
|
|
101
|
-
expect(snap?.recentErrorCount).toBe(4);
|
|
102
|
-
|
|
103
|
-
// 5 => dead + cooldown
|
|
104
|
-
await store.record(
|
|
105
|
-
err("openai", "timeout", new Date((nowMs += 10))),
|
|
106
|
-
);
|
|
107
|
-
snap = await store.getProvider("openai");
|
|
108
|
-
expect(snap?.health).toBe("dead");
|
|
109
|
-
expect(snap?.cooldownUntil).toBeGreaterThan(nowMs);
|
|
110
|
-
expect(snap?.healthReason).toMatch(/Error budget exhausted|Cooldown/);
|
|
111
|
-
|
|
112
|
-
// Still dead during cooldown even if window would eventually age out
|
|
113
|
-
nowMs += 5_000;
|
|
114
|
-
snap = await store.getProvider("openai");
|
|
115
|
-
expect(snap?.health).toBe("dead");
|
|
116
|
-
|
|
117
|
-
// After cooldown, and with aged-out errors, recovers to healthy
|
|
118
|
-
nowMs += 40_000;
|
|
119
|
-
snap = await store.getProvider("openai");
|
|
120
|
-
expect(snap?.health).toBe("healthy");
|
|
121
|
-
expect(snap?.cooldownUntil).toBeUndefined();
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it("marks hard auth failure as dead immediately with cooldown", async () => {
|
|
125
|
-
const t0 = Date.UTC(2026, 0, 2, 0, 0, 0);
|
|
126
|
-
let nowMs = t0;
|
|
127
|
-
const store = new InMemoryUsageStore({
|
|
128
|
-
cooldownMs: 15_000,
|
|
129
|
-
now: () => new Date(nowMs),
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
const result = await store.record({
|
|
133
|
-
providerId: "gemini",
|
|
134
|
-
authTier: "subscription",
|
|
135
|
-
kind: "auth_failure",
|
|
136
|
-
at: new Date(nowMs),
|
|
137
|
-
});
|
|
138
|
-
expect(result.snapshot.health).toBe("dead");
|
|
139
|
-
expect(result.healthChanged).toBe(true);
|
|
140
|
-
expect(result.previousHealth).toBe("healthy");
|
|
141
|
-
|
|
142
|
-
nowMs += 20_000;
|
|
143
|
-
const snap = await store.getProvider("gemini");
|
|
144
|
-
// No recent window errors beyond the single err once pruned? still within window.
|
|
145
|
-
// Cooldown expired; single error is within healthy budget (default 2).
|
|
146
|
-
expect(snap?.health).toBe("healthy");
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it("records subscription quota exhaustion separately from API-key spend", async () => {
|
|
150
|
-
const store = new InMemoryUsageStore();
|
|
151
|
-
|
|
152
|
-
await store.setSubscriptionQuota("claude", {
|
|
153
|
-
limit: 10,
|
|
154
|
-
used: 9,
|
|
155
|
-
status: "active",
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
// One more subscription unit exhausts.
|
|
159
|
-
await store.record({
|
|
160
|
-
providerId: "claude",
|
|
161
|
-
authTier: "subscription",
|
|
162
|
-
kind: "subscription_usage",
|
|
163
|
-
amount: 1,
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
let snap = await store.getProvider("claude");
|
|
167
|
-
expect(snap?.subscriptionStatus).toBe("exhausted");
|
|
168
|
-
expect(snap?.subscriptionQuotaRemaining).toBe(0);
|
|
169
|
-
expect(snap?.apiKeySpendUsd).toBe(0);
|
|
170
|
-
// Exhaustion alone is degraded, not necessarily dead.
|
|
171
|
-
expect(snap?.health).toBe("degraded");
|
|
172
|
-
|
|
173
|
-
// Paid fallback spend is tracked independently.
|
|
174
|
-
await store.record({
|
|
175
|
-
providerId: "claude",
|
|
176
|
-
authTier: "api-key",
|
|
177
|
-
kind: "api_key_spend",
|
|
178
|
-
amount: 0.03,
|
|
179
|
-
});
|
|
180
|
-
await store.record({
|
|
181
|
-
providerId: "claude",
|
|
182
|
-
authTier: "api-key",
|
|
183
|
-
kind: "api_key_spend",
|
|
184
|
-
amount: 0.02,
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
snap = await store.getProvider("claude");
|
|
188
|
-
expect(snap?.apiKeySpendUsd).toBeCloseTo(0.05, 8);
|
|
189
|
-
// Subscription counters unchanged by api-key spend.
|
|
190
|
-
expect(snap?.subscriptionQuotaUsed).toBe(10);
|
|
191
|
-
expect(snap?.subscriptionStatus).toBe("exhausted");
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it("explains why paid fallback was or was not used", async () => {
|
|
195
|
-
const store = new InMemoryUsageStore();
|
|
196
|
-
|
|
197
|
-
// Healthy subscription with remaining quota => prefer subscription.
|
|
198
|
-
await store.setSubscriptionQuota("claude", {
|
|
199
|
-
limit: 100,
|
|
200
|
-
used: 10,
|
|
201
|
-
status: "active",
|
|
202
|
-
});
|
|
203
|
-
let explain = await store.explainPaidFallback("claude", {
|
|
204
|
-
hasApiKeyCandidate: true,
|
|
205
|
-
});
|
|
206
|
-
expect(explain.recommendation).toBe("prefer_subscription");
|
|
207
|
-
expect(explain.subscriptionQuotaRemaining).toBe(90);
|
|
208
|
-
expect(explain.usedPaidFallback).toBe(false);
|
|
209
|
-
expect(explain.reason).toMatch(/should not be used yet|preferred/i);
|
|
210
|
-
|
|
211
|
-
// Exhaust subscription => allow API-key fallback.
|
|
212
|
-
await store.setSubscriptionQuota("claude", {
|
|
213
|
-
limit: 100,
|
|
214
|
-
used: 100,
|
|
215
|
-
status: "exhausted",
|
|
216
|
-
});
|
|
217
|
-
explain = await store.explainPaidFallback("claude", {
|
|
218
|
-
hasApiKeyCandidate: true,
|
|
219
|
-
});
|
|
220
|
-
expect(explain.recommendation).toBe("allow_api_key_fallback");
|
|
221
|
-
expect(explain.reason).toMatch(/exhausted/i);
|
|
222
|
-
|
|
223
|
-
// After paid spend, explanation notes fallback was used.
|
|
224
|
-
await store.record({
|
|
225
|
-
providerId: "claude",
|
|
226
|
-
authTier: "api-key",
|
|
227
|
-
kind: "api_key_spend",
|
|
228
|
-
amount: 1.25,
|
|
229
|
-
});
|
|
230
|
-
explain = await store.explainPaidFallback("claude", {
|
|
231
|
-
hasApiKeyCandidate: true,
|
|
232
|
-
});
|
|
233
|
-
expect(explain.apiKeySpendUsd).toBe(1.25);
|
|
234
|
-
expect(explain.usedPaidFallback).toBe(true);
|
|
235
|
-
expect(explain.subscriptionQuotaRemaining).toBe(0);
|
|
236
|
-
|
|
237
|
-
// Distinguishes unknown / no subscription data + api-key only.
|
|
238
|
-
explain = await store.explainPaidFallback("unknown-provider", {
|
|
239
|
-
hasApiKeyCandidate: true,
|
|
240
|
-
});
|
|
241
|
-
expect(explain.recommendation).toBe("api_key_only");
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it("toJSON never includes secrets or raw prompts", async () => {
|
|
245
|
-
const store = new InMemoryUsageStore();
|
|
246
|
-
await store.record({
|
|
247
|
-
providerId: "openai",
|
|
248
|
-
authTier: "api-key",
|
|
249
|
-
kind: "api_key_spend",
|
|
250
|
-
amount: 0.5,
|
|
251
|
-
detail: "sk-should-not-appear-in-json-anyway-but-caller-redacts",
|
|
252
|
-
});
|
|
253
|
-
const json = JSON.stringify(store.toJSON());
|
|
254
|
-
expect(json).not.toMatch(/sk-/);
|
|
255
|
-
expect(store.toJSON()).toMatchObject({
|
|
256
|
-
kind: "in-memory-usage",
|
|
257
|
-
providers: {
|
|
258
|
-
openai: {
|
|
259
|
-
apiKeySpendUsd: 0.5,
|
|
260
|
-
},
|
|
261
|
-
},
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
describe("InMemoryStateStore", () => {
|
|
267
|
-
it("composes audit + usage under one StateStore-style handle", async () => {
|
|
268
|
-
const state = new InMemoryStateStore();
|
|
269
|
-
await state.usage.setSubscriptionQuota("claude", {
|
|
270
|
-
limit: 5,
|
|
271
|
-
used: 5,
|
|
272
|
-
status: "exhausted",
|
|
273
|
-
});
|
|
274
|
-
const explain = await state.usage.explainPaidFallback("claude", {
|
|
275
|
-
hasApiKeyCandidate: true,
|
|
276
|
-
});
|
|
277
|
-
expect(explain.recommendation).toBe("allow_api_key_fallback");
|
|
278
|
-
|
|
279
|
-
const json = state.toJSON();
|
|
280
|
-
expect(json).toMatchObject({ kind: "in-memory-state" });
|
|
281
|
-
expect(JSON.stringify(json)).not.toMatch(/sk-/);
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
describe("near-limit / per-account usage", () => {
|
|
286
|
-
it("recordUsage returns near-limit snapshot when quota is nearly consumed", async () => {
|
|
287
|
-
const store = new InMemoryUsageStore();
|
|
288
|
-
const snap = await store.recordUsage("anthropic", "anthropic-1", { used: 85, limit: 100 });
|
|
289
|
-
expect(snap.remaining).toBe(15);
|
|
290
|
-
expect(snap.percentRemaining).toBe(15);
|
|
291
|
-
expect(snap.nearLimit).toBe(true);
|
|
292
|
-
expect(snap.subscriptionStatus).toBe("near-limit");
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it("isNearLimit respects custom threshold", async () => {
|
|
296
|
-
const store = new InMemoryUsageStore();
|
|
297
|
-
await store.recordUsage("anthropic", "anthropic-1", { used: 85, limit: 100 });
|
|
298
|
-
expect(await store.isNearLimit("anthropic", "anthropic-1")).toBe(true);
|
|
299
|
-
expect(await store.isNearLimit("anthropic", "anthropic-1", 0.1)).toBe(false);
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
it("recordUsage detects exhaustion", async () => {
|
|
303
|
-
const store = new InMemoryUsageStore();
|
|
304
|
-
const snap = await store.recordUsage("anthropic", "anthropic-2", { used: 100, limit: 100 });
|
|
305
|
-
expect(snap.nearLimit).toBe(false);
|
|
306
|
-
expect(snap.subscriptionStatus).toBe("exhausted");
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
it("getNearLimitAccounts returns only accounts near their limit", async () => {
|
|
310
|
-
const store = new InMemoryUsageStore();
|
|
311
|
-
await store.recordUsage("anthropic", "anthropic-1", { used: 85, limit: 100 });
|
|
312
|
-
await store.recordUsage("anthropic", "anthropic-2", { used: 100, limit: 100 });
|
|
313
|
-
const nearLimit = await store.getNearLimitAccounts();
|
|
314
|
-
expect(nearLimit).toHaveLength(1);
|
|
315
|
-
expect(nearLimit[0]!.accountId).toBe("anthropic-1");
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
it("getAccountUsage returns correct snapshots for different accounts", async () => {
|
|
319
|
-
const store = new InMemoryUsageStore();
|
|
320
|
-
await store.recordUsage("anthropic", "anthropic-1", { used: 85, limit: 100 });
|
|
321
|
-
await store.recordUsage("anthropic", "anthropic-2", { used: 10, limit: 100 });
|
|
322
|
-
const snap1 = await store.getAccountUsage("anthropic", "anthropic-1");
|
|
323
|
-
const snap2 = await store.getAccountUsage("anthropic", "anthropic-2");
|
|
324
|
-
expect(snap1?.used).toBe(85);
|
|
325
|
-
expect(snap2?.used).toBe(10);
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
it("existing setSubscriptionQuota and record still behave as before", async () => {
|
|
329
|
-
const store = new InMemoryUsageStore();
|
|
330
|
-
await store.setSubscriptionQuota("test", { limit: 50, used: 10, status: "active" });
|
|
331
|
-
const snap = await store.getProvider("test");
|
|
332
|
-
expect(snap?.subscriptionQuotaLimit).toBe(50);
|
|
333
|
-
expect(snap?.subscriptionQuotaUsed).toBe(10);
|
|
334
|
-
});
|
|
335
|
-
});
|