Files
oh-my-pi/packages/ai/test/openai-codex-usage.test.ts
T
can1357 212e05dbbb feat: added Codex saved reset-credit redemption flow to usage tooling
- Added Codex reset-credit models, endpoints, and redemption methods.
- Added reset-credit usage data and cache invalidation for successful redemption.
- Added `/reset-usage` slash command and interactive account selector flow.
- Added contract tests for reset-credit listing, fallback, and consume outcomes.
2026-06-12 05:20:08 +02:00

153 lines
5.8 KiB
TypeScript

/**
* Codex usage parser regressions. The widget client (osx-widgets) keys spark
* detection off `limit.id.includes("spark")`, so the parser MUST surface
* `additional_rate_limits[].metered_feature == "codex_bengalfox"` (the upstream
* codename for GPT-5.3-Codex-Spark) as separate `UsageLimit` entries with
* `spark` in the id. If this contract breaks, both the TUI and the macOS
* widget lose per-model visibility.
*/
import { describe, expect, it } from "bun:test";
import type { FetchImpl } from "@oh-my-pi/pi-ai/types";
import { openaiCodexUsageProvider } from "@oh-my-pi/pi-ai/usage/openai-codex";
const accessTokenFixture = (() => {
const header = Buffer.from(JSON.stringify({ alg: "none", typ: "JWT" })).toString("base64url");
const body = Buffer.from(
JSON.stringify({
"https://api.openai.com/auth": { chatgpt_account_id: "acct-fixture" },
"https://api.openai.com/profile": { email: "fixture@example.com" },
}),
).toString("base64url");
return `${header}.${body}.sig`;
})();
function makePayload() {
return {
plan_type: "pro",
rate_limit: {
allowed: true,
limit_reached: false,
primary_window: { used_percent: 4, limit_window_seconds: 17940, reset_at: 2_000_000_000 },
secondary_window: { used_percent: 1, limit_window_seconds: 604740, reset_at: 2_000_500_000 },
},
additional_rate_limits: [
{
limit_name: "GPT-5.3-Codex-Spark",
metered_feature: "codex_bengalfox",
rate_limit: {
allowed: true,
limit_reached: false,
primary_window: { used_percent: 17, limit_window_seconds: 18000, reset_at: 2_000_001_000 },
secondary_window: { used_percent: 61, limit_window_seconds: 604800, reset_at: 2_000_600_000 },
},
},
],
};
}
function fakeFetch(payload: unknown): FetchImpl {
const fn = async () =>
new Response(JSON.stringify(payload), { status: 200, headers: { "content-type": "application/json" } });
return fn as unknown as typeof fetch;
}
describe("openai-codex usage parser", () => {
it("emits primary + secondary limits from the main rate_limit block", async () => {
const report = await openaiCodexUsageProvider.fetchUsage(
{
provider: "openai-codex",
credential: { type: "oauth", accessToken: accessTokenFixture, accountId: "acct-1", email: "u@example.com" },
},
{ fetch: fakeFetch(makePayload()) },
);
expect(report).not.toBeNull();
const main = report?.limits.filter(l => l.id === "openai-codex:primary" || l.id === "openai-codex:secondary");
expect(main?.map(l => l.id)).toEqual(["openai-codex:primary", "openai-codex:secondary"]);
expect(main?.[0].scope.tier).toBe("pro");
expect(main?.[0].amount.usedFraction).toBeCloseTo(0.04, 5);
});
it("surfaces additional_rate_limits as spark UsageLimit entries the widget can detect", async () => {
const report = await openaiCodexUsageProvider.fetchUsage(
{
provider: "openai-codex",
credential: { type: "oauth", accessToken: accessTokenFixture, accountId: "acct-1", email: "u@example.com" },
},
{ fetch: fakeFetch(makePayload()) },
);
const spark = report?.limits.filter(l => l.id.includes("spark"));
expect(spark?.map(l => l.id)).toEqual(["openai-codex:spark:primary", "openai-codex:spark:secondary"]);
expect(spark?.[0].label).toBe("5 hours (Spark)");
expect(spark?.[1].label).toBe("7 days (Spark)");
expect(spark?.[0].scope.tier).toBe("spark");
expect(spark?.[0].scope.modelId).toBe("GPT-5.3-Codex-Spark");
expect(spark?.[0].amount.usedFraction).toBeCloseTo(0.17, 5);
expect(spark?.[1].amount.usedFraction).toBeCloseTo(0.61, 5);
});
it("treats bengalfox codename as spark even without explicit limit_name", async () => {
const payload = makePayload();
payload.additional_rate_limits[0].limit_name = undefined as unknown as string;
const report = await openaiCodexUsageProvider.fetchUsage(
{
provider: "openai-codex",
credential: { type: "oauth", accessToken: accessTokenFixture, accountId: "acct-1", email: "u@example.com" },
},
{ fetch: fakeFetch(payload) },
);
const spark = report?.limits.find(l => l.id === "openai-codex:spark:primary");
expect(spark).toBeTruthy();
expect(spark?.scope.tier).toBe("spark");
});
it("returns a report even when only additional_rate_limits are present (no main rate_limit)", async () => {
const payload = {
plan_type: "pro",
rate_limit: null,
additional_rate_limits: [
{
limit_name: "GPT-5.3-Codex-Spark",
metered_feature: "codex_bengalfox",
rate_limit: {
allowed: true,
limit_reached: false,
primary_window: { used_percent: 5, limit_window_seconds: 18000, reset_at: 2_000_000_000 },
},
},
],
};
const report = await openaiCodexUsageProvider.fetchUsage(
{
provider: "openai-codex",
credential: { type: "oauth", accessToken: accessTokenFixture, accountId: "acct-1", email: "u@example.com" },
},
{ fetch: fakeFetch(payload) },
);
expect(report).not.toBeNull();
expect(report?.limits.map(l => l.id)).toEqual(["openai-codex:spark:primary"]);
});
it("surfaces rate_limit_reset_credits.available_count as report.resetCredits", async () => {
const payload = { ...makePayload(), rate_limit_reset_credits: { available_count: 1 } };
const report = await openaiCodexUsageProvider.fetchUsage(
{
provider: "openai-codex",
credential: { type: "oauth", accessToken: accessTokenFixture, accountId: "acct-1", email: "u@example.com" },
},
{ fetch: fakeFetch(payload) },
);
expect(report?.resetCredits).toEqual({ availableCount: 1 });
});
it("omits resetCredits when the account has no saved resets block", async () => {
const report = await openaiCodexUsageProvider.fetchUsage(
{
provider: "openai-codex",
credential: { type: "oauth", accessToken: accessTokenFixture, accountId: "acct-1", email: "u@example.com" },
},
{ fetch: fakeFetch(makePayload()) },
);
expect(report?.resetCredits).toBeUndefined();
});
});