From 0cdac2c3b2e0d1bcf217390382011c9bb0206145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E5=98=89=E4=BC=9F?= <202383014@uibe.edu.cn> Date: Sun, 11 Oct 2026 16:17:23 +0800 Subject: [PATCH 1/2] fix(usage): reject pre-cancelled quota waits --- extensions/usage/providers/utils.ts | 1 + tests/extensions/usage/providers.test.ts | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/extensions/usage/providers/utils.ts b/extensions/usage/providers/utils.ts index 4b4923d36..c6ac1ddfe 100644 --- a/extensions/usage/providers/utils.ts +++ b/extensions/usage/providers/utils.ts @@ -82,6 +82,7 @@ export async function waitForUsage( promise: Promise, signal: AbortSignal, ): Promise { + signal.throwIfAborted(); let onAbort: (() => void) | undefined; const aborted = new Promise((_resolve, reject) => { onAbort = () => reject(signal.reason); diff --git a/tests/extensions/usage/providers.test.ts b/tests/extensions/usage/providers.test.ts index 858fc48b7..e4cae8cb5 100644 --- a/tests/extensions/usage/providers.test.ts +++ b/tests/extensions/usage/providers.test.ts @@ -11,6 +11,7 @@ import { DEFAULT_ADAPTERS } from "../../../extensions/usage/providers/registry.t import { computeUsageStatus, parseTimestamp, + waitForUsage, } from "../../../extensions/usage/providers/utils.ts"; function jwt(payload: Record) { @@ -25,6 +26,17 @@ function payloadFetch(payload: unknown): typeof fetch { return async () => Response.json(payload); } +test("a pre-cancelled quota wait cannot return an already-fulfilled result", async () => { + const controller = new AbortController(); + const reason = new Error("cancelled fixture"); + controller.abort(reason); + + await assert.rejects( + waitForUsage(Promise.resolve("stale quota"), controller.signal), + (error: unknown) => error === reason, + ); +}); + test("Cursor uses its session cookie, authoritative percentages and independent account metadata", async () => { const calls: Array<{ url: string; init?: RequestInit }> = []; const report = await cursorAdapter.fetchUsage( From 162109c6e1fb6918a8c31f6b442c97a3a045d04c Mon Sep 17 00:00:00 2001 From: tt-a1i Date: Tue, 13 Oct 2026 12:25:15 +0800 Subject: [PATCH 2/2] fix(usage): observe rejected input on pre-cancelled waits --- extensions/usage/providers/utils.ts | 2 + tests/extensions/usage/providers.test.ts | 66 ++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/extensions/usage/providers/utils.ts b/extensions/usage/providers/utils.ts index c6ac1ddfe..b6324f369 100644 --- a/extensions/usage/providers/utils.ts +++ b/extensions/usage/providers/utils.ts @@ -82,6 +82,8 @@ export async function waitForUsage( promise: Promise, signal: AbortSignal, ): Promise { + // The accepted refresh keeps running even when this wait is already cancelled. + if (signal.aborted) void promise.catch(() => undefined); signal.throwIfAborted(); let onAbort: (() => void) | undefined; const aborted = new Promise((_resolve, reject) => { diff --git a/tests/extensions/usage/providers.test.ts b/tests/extensions/usage/providers.test.ts index e4cae8cb5..9bcfd5267 100644 --- a/tests/extensions/usage/providers.test.ts +++ b/tests/extensions/usage/providers.test.ts @@ -1,4 +1,5 @@ import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; import test from "node:test"; import { getAntigravityUserAgent } from "../../../extensions/ai-providers/antigravity/oauth.ts"; import { @@ -37,6 +38,71 @@ test("a pre-cancelled quota wait cannot return an already-fulfilled result", asy ); }); +for (const timing of ["immediate", "delayed", "during-wait"]) { + test(`cancelled quota waits observe ${timing} rejection without leaking listeners`, () => { + const utilsUrl = new URL( + "../../../extensions/usage/providers/utils.ts", + import.meta.url, + ).href; + const result = spawnSync( + process.execPath, + [ + "--experimental-strip-types", + "--input-type=module", + "--eval", + ` + import assert from "node:assert/strict"; + import { getEventListeners } from "node:events"; + import { waitForUsage } from ${JSON.stringify(utilsUrl)}; + const timing = ${JSON.stringify(timing)}; + const unhandled = []; + process.on("unhandledRejection", (error) => unhandled.push(error)); + const controller = new AbortController(); + const reason = new Error("cancelled fixture"); + const failure = new Error("quota failed fixture"); + let rejectInput; + const input = timing === "immediate" + ? Promise.reject(failure) + : new Promise((_resolve, reject) => { rejectInput = reject; }); + if (timing !== "during-wait") controller.abort(reason); + const waiting = waitForUsage(input, controller.signal); + if (timing === "during-wait") { + assert.equal(getEventListeners(controller.signal, "abort").length, 1); + controller.abort(reason); + } else { + assert.equal(getEventListeners(controller.signal, "abort").length, 0); + } + await assert.rejects(waiting, (error) => error === reason); + assert.equal(getEventListeners(controller.signal, "abort").length, 0); + rejectInput?.(failure); + await new Promise((resolve) => setImmediate(resolve)); + await new Promise((resolve) => setImmediate(resolve)); + assert.deepEqual(unhandled, []); + process.stdout.write("observed"); + `, + ], + { encoding: "utf8", timeout: 10_000 }, + ); + assert.equal(result.error, undefined); + assert.equal(result.status, 0, result.stderr); + assert.equal(result.stdout, "observed"); + }); +} + +test("uncancelled quota waits preserve resolved values and rejected errors", async () => { + const controller = new AbortController(); + const value = { quota: 42 }; + assert.equal( + await waitForUsage(Promise.resolve(value), controller.signal), + value, + ); + const failure = new Error("quota failed fixture"); + await assert.rejects( + waitForUsage(Promise.reject(failure), controller.signal), + (error: unknown) => error === failure, + ); +}); + test("Cursor uses its session cookie, authoritative percentages and independent account metadata", async () => { const calls: Array<{ url: string; init?: RequestInit }> = []; const report = await cursorAdapter.fetchUsage(