This commit is contained in:
@@ -148,6 +148,7 @@ test("expired token skips fetch and keeps last snapshot", async () => {
|
||||
},
|
||||
60_000,
|
||||
path,
|
||||
1,
|
||||
);
|
||||
await tracker.init();
|
||||
await writeFile(path, credentials(Date.now() - 1));
|
||||
@@ -156,3 +157,134 @@ test("expired token skips fetch and keeps last snapshot", async () => {
|
||||
expect(tracker.status()?.windows).toHaveLength(4);
|
||||
await tracker.dispose();
|
||||
});
|
||||
|
||||
test("failed fetch retries with re-read credentials", async () => {
|
||||
await writeFile(path, credentials(Date.now() + 3_600_000));
|
||||
const tokens: Array<string | null> = [];
|
||||
let changed = 0;
|
||||
const tracker = new ClaudeQuotaTracker(
|
||||
async () => {
|
||||
changed++;
|
||||
},
|
||||
async (_input, init) => {
|
||||
tokens.push(new Headers(init?.headers).get("authorization"));
|
||||
if (tokens.length > 1) return Response.json(payload);
|
||||
await writeFile(
|
||||
path,
|
||||
credentials(Date.now() + 3_600_000).replace("token-123", "token-456"),
|
||||
);
|
||||
throw new Error("network down");
|
||||
},
|
||||
60_000,
|
||||
path,
|
||||
1,
|
||||
);
|
||||
await tracker.init();
|
||||
expect(tokens).toEqual(["Bearer token-123", "Bearer token-456"]);
|
||||
expect(tracker.status()?.plan).toBe("max");
|
||||
expect(changed).toBe(1);
|
||||
await tracker.dispose();
|
||||
});
|
||||
|
||||
test("corrupt credentials file keeps previous status and retries", async () => {
|
||||
await writeFile(path, credentials(Date.now() + 3_600_000));
|
||||
let calls = 0;
|
||||
let changed = 0;
|
||||
const tracker = new ClaudeQuotaTracker(
|
||||
async () => {
|
||||
changed++;
|
||||
},
|
||||
async () => {
|
||||
calls++;
|
||||
return Response.json(payload);
|
||||
},
|
||||
60_000,
|
||||
path,
|
||||
1,
|
||||
);
|
||||
await tracker.init();
|
||||
await writeFile(path, "{not json");
|
||||
await tracker.refresh();
|
||||
await writeFile(path, JSON.stringify({ claudeAiOauth: { accessToken: 1 } }));
|
||||
await tracker.refresh();
|
||||
expect(calls).toBe(1);
|
||||
expect(changed).toBe(1);
|
||||
expect(tracker.status()?.windows).toHaveLength(4);
|
||||
await tracker.dispose();
|
||||
});
|
||||
|
||||
test("missing credentials file clears status without retry", async () => {
|
||||
await writeFile(path, credentials(Date.now() + 3_600_000));
|
||||
let calls = 0;
|
||||
let changed = 0;
|
||||
const tracker = new ClaudeQuotaTracker(
|
||||
async () => {
|
||||
changed++;
|
||||
},
|
||||
async () => {
|
||||
calls++;
|
||||
return Response.json(payload);
|
||||
},
|
||||
60_000,
|
||||
path,
|
||||
10_000,
|
||||
);
|
||||
await tracker.init();
|
||||
await rm(path);
|
||||
const start = performance.now();
|
||||
await tracker.refresh();
|
||||
expect(performance.now() - start).toBeLessThan(1_000);
|
||||
expect(calls).toBe(1);
|
||||
expect(changed).toBe(2);
|
||||
expect(tracker.status()).toBeUndefined();
|
||||
await tracker.dispose();
|
||||
});
|
||||
|
||||
test("gives up after five failed attempts", async () => {
|
||||
await writeFile(path, credentials(Date.now() + 3_600_000));
|
||||
let calls = 0;
|
||||
const tracker = new ClaudeQuotaTracker(
|
||||
async () => {},
|
||||
async () => {
|
||||
calls++;
|
||||
return new Response("nope", { status: 500 });
|
||||
},
|
||||
60_000,
|
||||
path,
|
||||
1,
|
||||
);
|
||||
await tracker.init();
|
||||
expect(calls).toBe(5);
|
||||
expect(tracker.status()).toBeUndefined();
|
||||
await tracker.refresh();
|
||||
expect(calls).toBe(10);
|
||||
await tracker.dispose();
|
||||
});
|
||||
|
||||
test("dispose during backoff resolves quickly", async () => {
|
||||
await writeFile(path, credentials(Date.now() + 3_600_000));
|
||||
let calls = 0;
|
||||
let changed = 0;
|
||||
const tracker = new ClaudeQuotaTracker(
|
||||
async () => {
|
||||
changed++;
|
||||
},
|
||||
async () => {
|
||||
calls++;
|
||||
throw new Error("network down");
|
||||
},
|
||||
60_000,
|
||||
path,
|
||||
60_000,
|
||||
);
|
||||
const init = tracker.init();
|
||||
while (calls === 0) await new Promise((resolve) => setTimeout(resolve, 1));
|
||||
await new Promise((resolve) => setTimeout(resolve, 5));
|
||||
const start = performance.now();
|
||||
await tracker.dispose();
|
||||
await init;
|
||||
expect(performance.now() - start).toBeLessThan(1_000);
|
||||
expect(calls).toBe(1);
|
||||
expect(changed).toBe(0);
|
||||
expect(tracker.status()).toBeUndefined();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user