Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions src/providers/quota.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ export interface ProviderQuotaWindow {
resetAt?: number;
}

export interface ProviderQuotaCreditsUsd {
used: number;
limit: number;
remaining: number;
percent: number;
expiresAt?: number;
unlimited?: boolean;
}

export interface ProviderQuota {
fiveHourPercent?: number;
fiveHourResetAt?: number;
Expand All @@ -72,6 +81,7 @@ export interface ProviderQuota {
monthlyPercent?: number;
monthlyResetAt?: number;
customWindows?: ProviderQuotaWindow[];
creditsUsd?: ProviderQuotaCreditsUsd;
updatedAt: number;
}

Expand Down Expand Up @@ -203,6 +213,8 @@ function hasQuotaRows(quota: ProviderQuota | null | undefined): quota is Provide
return typeof quota.fiveHourPercent === "number"
|| typeof quota.weeklyPercent === "number"
|| typeof quota.monthlyPercent === "number"
|| quota.creditsUsd?.unlimited === true
|| typeof quota.creditsUsd?.percent === "number"
|| !!quota.customWindows?.some(window => typeof window.percent === "number");
}

Expand Down Expand Up @@ -340,6 +352,27 @@ async function fetchA6apiQuota(provider: string, config: OcxProviderConfig): Pro
}
const subscription = a6apiPayload(await subscriptionResponse.json().catch(() => null));
const token = a6apiPayload(await tokenResponse.json().catch(() => null));
const unlimited = token?.unlimited_quota === true
|| token?.unlimited_quota === 1
|| token?.unlimited_quota === "true";
const normalizedExpiry = normalizeResetAt(token?.expires_at);
const expiry = normalizedExpiry && normalizedExpiry > 0
? { expiresAt: normalizedExpiry }
: {};
if (unlimited) {
return report(provider, "a6api:billing", {
creditsUsd: {
used: 0,
limit: 0,
remaining: 0,
percent: 0,
unlimited: true,
...expiry,
},
customWindows: [{ label: "Unlimited API credits", percent: 0 }],
updatedAt: Date.now(),
});
}
const limitUsd = firstFinite(subscription, ["hard_limit_usd"]);
const grantedUnits = firstFinite(token, ["total_granted"]);
const usedUnits = firstFinite(token, ["total_used"]);
Expand All @@ -362,6 +395,13 @@ async function fetchA6apiQuota(provider: string, config: OcxProviderConfig): Pro
if (percent === undefined) return TERMINAL_QUOTA_FAILURE;
const label = `API credits ($${remainingUsd.toFixed(2)} of $${limitUsd.toFixed(2)} remaining)`;
return report(provider, "a6api:billing", {
creditsUsd: {
used: usedUsd,
limit: limitUsd,
remaining: remainingUsd,
percent,
...expiry,
},
customWindows: [{ label, percent }],
updatedAt: Date.now(),
});
Expand Down
37 changes: 37 additions & 0 deletions tests/provider-quota.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,13 @@ describe("fetchProviderQuotaReports", () => {
label: "API credits ($15.00 of $20.00 remaining)",
percent: 25,
}]);
expect(result.reports[0]?.quota.creditsUsd).toEqual({
used: 5,
limit: 20,
remaining: 15,
percent: 25,
expiresAt: Date.parse("2026-08-01T00:00:00Z"),
});
expect(seen.map(row => row.url).sort()).toEqual([
"https://api.a6api.com/api/usage/token/",
"https://api.a6api.com/dashboard/billing/subscription",
Expand All @@ -290,6 +297,36 @@ describe("fetchProviderQuotaReports", () => {
expect(seen.every(row => row.redirect === "error")).toBe(true);
});

test("A6API unlimited keys remain visible even when all finite credit totals are zero", async () => {
globalThis.fetch = (async (input: RequestInfo | URL) => new Response(JSON.stringify(
String(input).includes("subscription")
? { data: { hard_limit_usd: 100_000_000 } }
: { data: {
total_granted: 0,
total_used: 0,
total_available: 0,
unlimited_quota: true,
expires_at: "2027-01-01T00:00:00Z",
} },
), { status: 200 })) as typeof fetch;

const result = await fetchProviderQuotaReports(a6apiOnlyConfig(), true);

expect(result.reports).toHaveLength(1);
expect(result.reports[0]?.quota.creditsUsd).toEqual({
used: 0,
limit: 0,
remaining: 0,
percent: 0,
unlimited: true,
expiresAt: Date.parse("2027-01-01T00:00:00Z"),
});
expect(result.reports[0]?.quota.customWindows).toEqual([{
label: "Unlimited API credits",
percent: 0,
}]);
});
Comment on lines +300 to +328

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Test expiry propagation for unlimited accounts.

Line 309 sets expires_at to 0. The test cannot detect a regression that drops expiresAt from the unlimited creditsUsd branch.

Set a valid timestamp and assert expiresAt in the expected quota object.

Proposed test update
-          expires_at: 0,
+          expires_at: "2027-01-01T00:00:00Z",
...
       percent: 0,
       unlimited: true,
+      expiresAt: Date.parse("2027-01-01T00:00:00Z"),

As per path instructions, a src/ behavior change must have a focused regression test under tests/.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test("A6API unlimited keys remain visible even when all finite credit totals are zero", async () => {
globalThis.fetch = (async (input: RequestInfo | URL) => new Response(JSON.stringify(
String(input).includes("subscription")
? { data: { hard_limit_usd: 100_000_000 } }
: { data: {
total_granted: 0,
total_used: 0,
total_available: 0,
unlimited_quota: true,
expires_at: 0,
} },
), { status: 200 })) as typeof fetch;
const result = await fetchProviderQuotaReports(a6apiOnlyConfig(), true);
expect(result.reports).toHaveLength(1);
expect(result.reports[0]?.quota.creditsUsd).toEqual({
used: 0,
limit: 0,
remaining: 0,
percent: 0,
unlimited: true,
});
expect(result.reports[0]?.quota.customWindows).toEqual([{
label: "Unlimited API credits",
percent: 0,
}]);
});
test("A6API unlimited keys remain visible even when all finite credit totals are zero", async () => {
globalThis.fetch = (async (input: RequestInfo | URL) => new Response(JSON.stringify(
String(input).includes("subscription")
? { data: { hard_limit_usd: 100_000_000 } }
: { data: {
total_granted: 0,
total_used: 0,
total_available: 0,
unlimited_quota: true,
expires_at: "2027-01-01T00:00:00Z",
} },
), { status: 200 })) as typeof fetch;
const result = await fetchProviderQuotaReports(a6apiOnlyConfig(), true);
expect(result.reports).toHaveLength(1);
expect(result.reports[0]?.quota.creditsUsd).toEqual({
used: 0,
limit: 0,
remaining: 0,
percent: 0,
unlimited: true,
expiresAt: Date.parse("2027-01-01T00:00:00Z"),
});
expect(result.reports[0]?.quota.customWindows).toEqual([{
label: "Unlimited API credits",
percent: 0,
}]);
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/provider-quota.test.ts` around lines 300 - 327, Update the test around
fetchProviderQuotaReports to use a valid nonzero expires_at timestamp in the
mocked unlimited-quota response, then assert that the returned creditsUsd object
includes the same expiresAt value alongside its existing fields. Keep the test
focused on propagating expiry for unlimited accounts.

Source: Path instructions


test("A6API quota never sends API keys to a non-canonical base URL", async () => {
const seen: string[] = [];
globalThis.fetch = (async (input: RequestInfo | URL) => {
Expand Down
Loading