Files
2026-07-13 13:39:12 +08:00

262 lines
7.8 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import {
fetchCodexQuota,
getCodexQuotaCooldownMs,
invalidateCodexQuotaCache,
registerCodexConnection,
registerCodexQuotaFetcher,
} from "../../open-sse/services/codexQuotaFetcher.ts";
import { preflightQuota } from "../../open-sse/services/quotaPreflight.ts";
import {
clearQuotaMonitors,
getActiveMonitorCount,
startQuotaMonitor,
stopQuotaMonitor,
} from "../../open-sse/services/quotaMonitor.ts";
import { clearSessions, touchSession } from "../../open-sse/services/sessionManager.ts";
const originalFetch = globalThis.fetch;
test.afterEach(() => {
globalThis.fetch = originalFetch;
clearQuotaMonitors();
clearSessions();
});
test("fetchCodexQuota returns null when no registered credentials exist", async () => {
const quota = await fetchCodexQuota(`missing-${Date.now()}`);
assert.equal(quota, null);
});
test("fetchCodexQuota can read credentials directly from the provided connection snapshot", async () => {
const connectionId = `codex-inline-${Date.now()}`;
const calls = [];
globalThis.fetch = async (url, init) => {
calls.push({ url, init });
return new Response(
JSON.stringify({
rate_limit: {
primary_window: {
used_percent: 70,
reset_after_seconds: 45,
},
secondary_window: {
used_percent: 20,
reset_after_seconds: 300,
},
},
}),
{
status: 200,
headers: { "content-type": "application/json" },
}
);
};
const quota = await fetchCodexQuota(connectionId, {
accessToken: "inline-token",
providerSpecificData: {
workspaceId: "workspace-inline",
},
});
assert.equal(calls.length, 1);
assert.equal(calls[0].init.headers.Authorization, "Bearer inline-token");
assert.equal(calls[0].init.headers["chatgpt-account-id"], "workspace-inline");
assert.equal(quota.percentUsed, 0.7);
assert.ok(typeof quota.resetAt === "string");
invalidateCodexQuotaCache(connectionId);
});
test("fetchCodexQuota parses dual-window usage, forwards workspace headers, and caches results", async () => {
const connectionId = `codex-cache-${Date.now()}`;
const calls = [];
registerCodexConnection(connectionId, {
accessToken: "access-token",
workspaceId: "workspace-123",
});
globalThis.fetch = async (url, init) => {
calls.push({ url, init });
return new Response(
JSON.stringify({
rate_limit: {
primary_window: {
used_percent: 80,
reset_after_seconds: 60,
},
secondary_window: {
used_percent: 40,
reset_at: Math.floor((Date.now() + 3600_000) / 1000),
},
},
}),
{
status: 200,
headers: { "content-type": "application/json" },
}
);
};
const first = await fetchCodexQuota(connectionId);
const second = await fetchCodexQuota(connectionId);
assert.equal(calls.length, 1);
assert.equal(calls[0].url, "https://chatgpt.com/backend-api/wham/usage");
assert.equal(calls[0].init.headers.Authorization, "Bearer access-token");
assert.equal(calls[0].init.headers["chatgpt-account-id"], "workspace-123");
assert.equal(first.percentUsed, 0.8);
assert.equal(first.window5h.percentUsed, 0.8);
assert.equal(first.window7d.percentUsed, 0.4);
assert.deepEqual(second, first);
invalidateCodexQuotaCache(connectionId);
});
test("fetchCodexQuota evaluates normal and Spark windows independently by requested model", async () => {
const connectionId = `codex-spark-scope-${Date.now()}`;
let calls = 0;
registerCodexConnection(connectionId, {
accessToken: "access-token-spark",
});
globalThis.fetch = async () => {
calls++;
return new Response(
JSON.stringify({
rate_limit: {
primary_window: { used_percent: 20, reset_after_seconds: 60 },
secondary_window: { used_percent: 30, reset_after_seconds: 120 },
},
additional_rate_limits: [
{
limit_id: "codex_bengalfox",
limit_name: "GPT-5.3-Codex-Spark",
metered_feature: "gpt_5_3_codex_spark",
rate_limit: {
primary_window: { used_percent: 100, reset_after_seconds: 300 },
secondary_window: { used_percent: 40, reset_after_seconds: 600 },
},
},
],
}),
{
status: 200,
headers: { "content-type": "application/json" },
}
);
};
const normal = await fetchCodexQuota(connectionId, { requestedModel: "gpt-5.3-codex" });
const spark = await fetchCodexQuota(connectionId, { requestedModel: "gpt-5.3-codex-spark" });
assert.equal(calls, 2, "normal and Spark scopes use separate cache entries");
assert.equal(normal.percentUsed, 0.3);
assert.equal(normal.windows?.session.percentUsed, 0.2);
assert.equal(normal.windows?.weekly.percentUsed, 0.3);
assert.equal(normal.windows?.gpt_5_3_codex_spark_session, undefined);
assert.equal(spark.percentUsed, 1);
assert.equal(spark.windows?.gpt_5_3_codex_spark_session.percentUsed, 1);
assert.equal(spark.windows?.gpt_5_3_codex_spark_weekly.percentUsed, 0.4);
assert.equal(spark.windows?.session, undefined);
const sparkCached = await fetchCodexQuota(connectionId, {
requestedModel: "gpt-5.3-codex-spark",
});
assert.equal(calls, 2);
assert.deepEqual(sparkCached, spark);
invalidateCodexQuotaCache(connectionId);
});
test("fetchCodexQuota drops bad credentials after an authorization failure", async () => {
const connectionId = `codex-auth-${Date.now()}`;
let calls = 0;
registerCodexConnection(connectionId, {
accessToken: "expired-token",
});
globalThis.fetch = async () => {
calls++;
return new Response("unauthorized", { status: 401 });
};
const first = await fetchCodexQuota(connectionId);
const second = await fetchCodexQuota(connectionId);
assert.equal(first, null);
assert.equal(second, null);
assert.equal(calls, 1);
});
test("getCodexQuotaCooldownMs prefers the 7d window before the 5h window", () => {
const now = Date.now();
const quota = {
used: 99,
total: 100,
percentUsed: 0.99,
window5h: {
percentUsed: 0.99,
resetAt: new Date(now + 60_000).toISOString(),
},
window7d: {
percentUsed: 0.97,
resetAt: new Date(now + 300_000).toISOString(),
},
limitReached: false,
};
const cooldownMs = getCodexQuotaCooldownMs(quota);
assert.ok(cooldownMs >= 295_000);
assert.ok(cooldownMs <= 300_000);
});
test("registerCodexQuotaFetcher exposes Codex quota to preflight and monitor flows", async () => {
const connectionId = `codex-preflight-${Date.now()}`;
registerCodexQuotaFetcher();
registerCodexConnection(connectionId, {
accessToken: "quota-token",
});
// Use 100% (fully exhausted) to avoid floating-point boundary issues:
// (1 - 0.98) * 100 = 2.0000000000000018, which is > DEFAULT_MIN_REMAINING_PERCENT (2),
// so the preflight wouldn't block. 100% used → 0% remaining, clearly below 2%.
globalThis.fetch = async () =>
new Response(
JSON.stringify({
rate_limit: {
primary_window: { used_percent: 100, reset_after_seconds: 90 },
},
}),
{
status: 200,
headers: { "content-type": "application/json" },
}
);
const preflight = await preflightQuota("codex", connectionId, {
providerSpecificData: { quotaPreflightEnabled: true },
});
touchSession("session-codex", connectionId);
startQuotaMonitor("session-codex", "codex", connectionId, {
providerSpecificData: { quotaMonitorEnabled: true },
});
assert.equal(preflight.proceed, false);
assert.equal(preflight.reason, "quota_exhausted");
assert.equal(getActiveMonitorCount(), 1);
stopQuotaMonitor("session-codex");
assert.equal(getActiveMonitorCount(), 0);
});