+31
-47
@@ -173,10 +173,6 @@ function fixture(
|
||||
storageReads: Promise<Schema.Json | undefined>[] = [],
|
||||
) {
|
||||
const stored = new Map<string, Schema.Json>();
|
||||
const sessions = new Map([
|
||||
["root", {}],
|
||||
["child", { parentID: "root" }],
|
||||
]);
|
||||
let calls = 0;
|
||||
let storageCalls = 0;
|
||||
let changes = 0;
|
||||
@@ -198,12 +194,6 @@ function fixture(
|
||||
},
|
||||
},
|
||||
} as never,
|
||||
session: {
|
||||
async get({ sessionID }: { sessionID: string }) {
|
||||
// SAFETY: QuotaTracker reads only parentID from this focused fixture.
|
||||
return sessions.get(sessionID) as never;
|
||||
},
|
||||
} as never,
|
||||
storage: {
|
||||
async get(key: string) {
|
||||
storageCalls++;
|
||||
@@ -242,6 +232,7 @@ function fixture(
|
||||
storageCalls: () => storageCalls,
|
||||
changes: () => changes,
|
||||
headers,
|
||||
stored,
|
||||
switch(credential: typeof current) {
|
||||
current = credential;
|
||||
},
|
||||
@@ -256,7 +247,7 @@ async function accountFingerprint(id: string) {
|
||||
return Buffer.from(bytes).toString("hex");
|
||||
}
|
||||
|
||||
test("removes cached Spark and representative reserve windows and consumption while offline", async () => {
|
||||
test("reads old cached snapshots without session estimates and removes excluded windows", async () => {
|
||||
const cached = {
|
||||
account: await accountFingerprint("account-one"),
|
||||
latest: {
|
||||
@@ -331,22 +322,23 @@ test("removes cached Spark and representative reserve windows and consumption wh
|
||||
);
|
||||
|
||||
await result.tracker.init();
|
||||
const status = await result.tracker.status("root");
|
||||
const status = result.tracker.status();
|
||||
expect(status?.windows.map((window) => window.id)).toEqual([
|
||||
"codex:primary",
|
||||
"codex_luna:primary",
|
||||
]);
|
||||
expect(status?.windows[0]?.sessionConsumed).toBe(2);
|
||||
expect(status?.windows[1]?.sessionConsumed).toBe(3);
|
||||
expect(status?.windows[0]).not.toHaveProperty("sessionConsumed");
|
||||
expect(result.stored.get("chatgpt-quota")).not.toHaveProperty("sessions");
|
||||
expect(result.calls()).toBe(0);
|
||||
await result.tracker.dispose();
|
||||
});
|
||||
|
||||
test("activates only for OAuth and establishes an awaited root baseline shared by children", async () => {
|
||||
test("activates only for OAuth and refreshes quota at request start", async () => {
|
||||
const key = fixture({ type: "key", key: "secret" }, [plus]);
|
||||
await key.tracker.init();
|
||||
await key.tracker.start("root");
|
||||
expect(key.calls()).toBe(0);
|
||||
expect(await key.tracker.status("root")).toBeUndefined();
|
||||
expect(key.tracker.status()).toBeUndefined();
|
||||
await key.tracker.dispose();
|
||||
|
||||
const oauth = fixture(
|
||||
@@ -372,13 +364,14 @@ test("activates only for OAuth and establishes an awaited root baseline shared b
|
||||
expect(oauth.calls()).toBe(1);
|
||||
expect(oauth.headers[0]?.get("chatgpt-account-id")).toBe("account-one");
|
||||
await oauth.tracker.refresh();
|
||||
const child = await oauth.tracker.status("child");
|
||||
expect(child?.windows[0]?.sessionConsumed).toBe(5);
|
||||
expect(child?.windows[0]?.remaining).toBe(82.5);
|
||||
const status = oauth.tracker.status();
|
||||
expect(status?.windows[0]?.remaining).toBe(82.5);
|
||||
expect(status?.windows[0]).not.toHaveProperty("sessionConsumed");
|
||||
expect(oauth.stored.get("chatgpt-quota")).not.toHaveProperty("sessions");
|
||||
await oauth.tracker.dispose();
|
||||
});
|
||||
|
||||
test("polls, carries consumption across quota resets, tolerates errors, and clears on logout", async () => {
|
||||
test("polls, tolerates errors, and clears on logout", async () => {
|
||||
const reset = {
|
||||
...plus,
|
||||
rate_limit: {
|
||||
@@ -403,12 +396,12 @@ test("polls, carries consumption across quota resets, tolerates errors, and clea
|
||||
await result.tracker.init();
|
||||
await result.tracker.start("root");
|
||||
await Bun.sleep(35);
|
||||
const status = await result.tracker.status("root");
|
||||
const status = result.tracker.status();
|
||||
expect(result.calls()).toBeGreaterThanOrEqual(3);
|
||||
expect(status?.windows[0]?.sessionConsumed).toBe(2);
|
||||
expect(status?.windows[0]?.remaining).toBe(98);
|
||||
result.logout();
|
||||
await result.tracker.authChanged(true);
|
||||
expect(await result.tracker.status("root")).toBeUndefined();
|
||||
expect(result.tracker.status()).toBeUndefined();
|
||||
const calls = result.calls();
|
||||
await result.tracker.dispose();
|
||||
await Bun.sleep(20);
|
||||
@@ -424,12 +417,11 @@ test("sidebar text places reset timing beside latest remaining quota", () => {
|
||||
label: null,
|
||||
used: 25,
|
||||
remaining: 75,
|
||||
sessionConsumed: 1.5,
|
||||
resetsAt: 100_000 + 2 * 86_400_000 + 5 * 3_600_000,
|
||||
},
|
||||
100_000,
|
||||
),
|
||||
).toBe("7d: 75% · ~1.5pp · 2d 5h");
|
||||
).toBe("7d: 75% · 2d 5h");
|
||||
});
|
||||
|
||||
test("sidebar reset timing uses compact future units and handles boundaries", () => {
|
||||
@@ -442,17 +434,16 @@ test("sidebar reset timing uses compact future units and handles boundaries", ()
|
||||
label: null,
|
||||
used: 25,
|
||||
remaining: 75,
|
||||
sessionConsumed: 1.5,
|
||||
resetsAt: now + resetIn,
|
||||
},
|
||||
now,
|
||||
);
|
||||
|
||||
expect(format(2 * 3_600_000 + 15 * 60_000)).toBe("5h: 75% · ~1.5pp · 2h 15m");
|
||||
expect(format(3_600_000)).toBe("5h: 75% · ~1.5pp · 1h");
|
||||
expect(format(15 * 60_000)).toBe("5h: 75% · ~1.5pp · 15m");
|
||||
expect(format(60_000)).toBe("5h: 75% · ~1.5pp · 1m");
|
||||
expect(format(59_999)).toBe("5h: 75% · ~1.5pp · <1m");
|
||||
expect(format(2 * 3_600_000 + 15 * 60_000)).toBe("5h: 75% · 2h 15m");
|
||||
expect(format(3_600_000)).toBe("5h: 75% · 1h");
|
||||
expect(format(15 * 60_000)).toBe("5h: 75% · 15m");
|
||||
expect(format(60_000)).toBe("5h: 75% · 1m");
|
||||
expect(format(59_999)).toBe("5h: 75% · <1m");
|
||||
});
|
||||
|
||||
test("sidebar reset timing preserves labels and distinguishes stale and unavailable timestamps", () => {
|
||||
@@ -462,17 +453,16 @@ test("sidebar reset timing preserves labels and distinguishes stale and unavaila
|
||||
label: "GPT-5.6 Luna",
|
||||
used: 99.95,
|
||||
remaining: 0.05,
|
||||
sessionConsumed: 1.5,
|
||||
};
|
||||
expect(formatQuotaWindow({ ...window, resetsAt: 999 }, 1_000)).toBe(
|
||||
"GPT-5.6 Luna 5h: <0.1% · ~1.5pp · due",
|
||||
"GPT-5.6 Luna 5h: <0.1% · due",
|
||||
);
|
||||
expect(formatQuotaWindow({ ...window, resetsAt: Number.NaN }, 1_000)).toBe(
|
||||
"GPT-5.6 Luna 5h: <0.1% · ~1.5pp · unavailable",
|
||||
"GPT-5.6 Luna 5h: <0.1% · unavailable",
|
||||
);
|
||||
});
|
||||
|
||||
test("inactive roots freeze, resume from a cached baseline on failure, and deactivate by family", async () => {
|
||||
test("request completion refreshes once across terminal and idle events", async () => {
|
||||
const used = (percent: number) => ({
|
||||
...plus,
|
||||
rate_limit: {
|
||||
@@ -492,16 +482,10 @@ test("inactive roots freeze, resume from a cached baseline on failure, and deact
|
||||
await result.tracker.init();
|
||||
await result.tracker.start("root");
|
||||
await result.tracker.complete("root");
|
||||
expect((await result.tracker.status("root"))?.windows[0]?.sessionConsumed).toBe(1);
|
||||
await result.tracker.refresh();
|
||||
expect((await result.tracker.status("root"))?.windows[0]?.sessionConsumed).toBe(1);
|
||||
|
||||
await result.tracker.start("root");
|
||||
await result.tracker.start("child");
|
||||
expect(result.tracker.status()?.windows[0]?.remaining).toBe(89);
|
||||
await result.tracker.complete("root");
|
||||
await result.tracker.refresh();
|
||||
await result.tracker.complete("child");
|
||||
expect((await result.tracker.status("root"))?.windows[0]?.sessionConsumed).toBe(6);
|
||||
expect(result.calls()).toBe(2);
|
||||
expect(result.tracker.status()?.windows[0]?.remaining).toBe(89);
|
||||
await result.tracker.dispose();
|
||||
});
|
||||
|
||||
@@ -542,10 +526,10 @@ test("an auth switch detaches an in-flight old-account response and immediately
|
||||
expect(result.calls()).toBe(2);
|
||||
pending.resolve(plus);
|
||||
await old;
|
||||
expect((await result.tracker.status("root"))?.windows[0]?.used).toBe(70);
|
||||
expect(result.tracker.status()?.windows[0]?.used).toBe(70);
|
||||
expect(result.headers[1]?.get("chatgpt-account-id")).toBe("account-two");
|
||||
await result.tracker.refresh();
|
||||
expect((await result.tracker.status("root"))?.windows[0]?.sessionConsumed).toBe(0);
|
||||
expect(result.tracker.status()?.windows[0]).not.toHaveProperty("sessionConsumed");
|
||||
await result.tracker.dispose();
|
||||
});
|
||||
|
||||
@@ -577,7 +561,7 @@ test("an auth switch during an old account storage read cannot restore stale sta
|
||||
expect(result.calls()).toBe(1);
|
||||
pending.resolve(undefined);
|
||||
await old;
|
||||
expect((await result.tracker.status("root"))?.windows[0]?.used).toBe(64);
|
||||
expect(result.tracker.status()?.windows[0]?.used).toBe(64);
|
||||
expect(result.headers[0]?.get("chatgpt-account-id")).toBe("account-two");
|
||||
await result.tracker.dispose();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user