mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-21 22:32:22 +03:00
* fix(usage) handle OpenRouter PAYG credit percentage OpenRouter PAYG accounts without a per-key limit previously rendered the credits row as 'total: 0, remainingPercentage: 100, unlimited: true', treating /credits balance as unlimited even when a real credit pool was present. Route the credit pool through the credits renderer with the real denominator: total = totalCredits when positive, used = total - creditBalance, remaining = creditBalance, remainingPercentage = round(balance / total * 100), isCredits: true, unlimited: false. Per-key limit still wins. A non-positive pool surfaces the row but never invents a 100% bar. Tests cover: explicit key limit, PAYG account credits without key limit, key limit taking priority over account credits, and a balance without a positive denominator. * fix(usage) render OpenRouter PAYG quota as a metered percentage bar The frontend parser was routing every OpenRouter 'credits' quota through buildCreditsQuota(), which sets isCredits: true. QuotaCardExpanded short-circuits on that flag and shows only the USD balance as a bare number, so a real PAYG payload (used: 7.33, total: 10, remaining: 2.67, remainingPercentage: 27) was rendered as '$2.67' instead of the '27% left / 7.33 / 10' bar the backend already computed. Drop isCredits: true for any payload whose total is a positive finite number - the row then goes through the normal normalizeQuotaEntry() path with currency preserved as an extra. The balance-only fallback (total 0 or non-finite denominator, used by legacy /credits responses) still uses buildCreditsQuota() so the row stays renderable, and never invents a 100% percentage. The frontend test now asserts: - PAYG positive denominator -> total: 10, remainingPercentage: 27, currency: 'USD', isCredits !== true. - Balance-only payload -> isCredits === true, creditCount === 2.67, total: 0, no fabricated 100%. - NaN denominator -> balance-only fallback. - Non-credits keys -> unchanged normalizeQuotaEntry() path. - Mixed payload -> normal quota row + PAYG row, both kept. * docs(changelog): add OpenRouter PAYG fix fragment * docs(changelog): remove self credit
131 lines
3.8 KiB
TypeScript
131 lines
3.8 KiB
TypeScript
/**
|
|
* Frontend parser regression #12468 follow-up.
|
|
*/
|
|
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import { parseQuotaData } from "../../src/app/(dashboard)/dashboard/usage/components/ProviderLimits/quotaParsing.ts";
|
|
|
|
const parseOpenrouter = (data: unknown) => parseQuotaData("openrouter", data) as QuotaRow[];
|
|
|
|
type QuotaRow = {
|
|
name: string;
|
|
used: number;
|
|
total: number;
|
|
remaining: number;
|
|
remainingPercentage?: number;
|
|
unlimited?: boolean;
|
|
isCredits?: boolean;
|
|
creditCount?: number;
|
|
currency?: string;
|
|
};
|
|
|
|
function findCredits(rows: QuotaRow[]): QuotaRow {
|
|
const row = rows.find((r: QuotaRow) => r.name === "credits");
|
|
assert.ok(row, "expected 'credits' row");
|
|
return row as QuotaRow;
|
|
}
|
|
|
|
test("parseOpenrouter renders PAYG account quota with normal percentage bar (no isCredits short-circuit)", () => {
|
|
const rows = parseOpenrouter({
|
|
quotas: {
|
|
credits: {
|
|
used: 7.332_573_982,
|
|
total: 10,
|
|
remaining: 2.667_426_018,
|
|
remainingPercentage: 27,
|
|
unlimited: false,
|
|
currency: "USD",
|
|
},
|
|
},
|
|
});
|
|
const row = findCredits(rows);
|
|
assert.equal(row.total, 10, "total must come from PAYG denominator");
|
|
assert.ok(Math.abs(row.used - 7.332_573_982) < 1e-6);
|
|
assert.ok(Math.abs(row.remaining - 2.667_426_018) < 1e-6);
|
|
assert.equal(row.remainingPercentage, 27);
|
|
assert.equal(row.currency, "USD");
|
|
// The credits renderer in QuotaCardExpanded short-circuits when isCredits
|
|
// is true and only shows the USD balance - a positive-denominator PAYG row
|
|
// must NOT take that branch.
|
|
assert.notEqual(row.isCredits, true, "PAYG row must not flip isCredits");
|
|
});
|
|
|
|
test("parseOpenrouter keeps credit-balance row when no positive denominator", () => {
|
|
const rows = parseOpenrouter({
|
|
quotas: {
|
|
credits: {
|
|
used: 0,
|
|
total: 0,
|
|
remaining: 2.67,
|
|
unlimited: false,
|
|
currency: "USD",
|
|
},
|
|
},
|
|
});
|
|
const row = findCredits(rows);
|
|
assert.equal(row.total, 0);
|
|
assert.equal(row.used, 0);
|
|
assert.equal(row.remaining, 2.67);
|
|
assert.equal(row.isCredits, true, "balance row keeps isCredits renderer");
|
|
assert.equal(row.creditCount, 2.67);
|
|
assert.notEqual(row.remainingPercentage, 100, "no fabricated 100% percentage");
|
|
});
|
|
|
|
test("parseOpenrouter falls back to balance row for non-finite denominator", () => {
|
|
const rows = parseOpenrouter({
|
|
quotas: {
|
|
credits: { used: 0, total: NaN, remaining: 1.5 },
|
|
},
|
|
});
|
|
const row = findCredits(rows);
|
|
assert.equal(row.isCredits, true);
|
|
assert.equal(row.remaining, 1.5);
|
|
});
|
|
|
|
test("parseOpenrouter keeps per-model rows alongside PAYG credit row", () => {
|
|
const rows = parseOpenrouter({
|
|
quotas: {
|
|
credits: {
|
|
used: 7.332_573_982,
|
|
total: 10,
|
|
remaining: 2.667_426_018,
|
|
remainingPercentage: 27,
|
|
unlimited: false,
|
|
currency: "USD",
|
|
},
|
|
"anthropic/claude-3.5-sonnet": {
|
|
used: 12,
|
|
total: 100,
|
|
remaining: 88,
|
|
resetAt: null,
|
|
},
|
|
},
|
|
});
|
|
assert.equal(rows.length, 2);
|
|
const credits = findCredits(rows);
|
|
assert.equal(credits.total, 10);
|
|
assert.ok(Math.abs(credits.used - 7.332_573_982) < 1e-6);
|
|
assert.equal(credits.remainingPercentage, 27);
|
|
assert.notEqual(credits.isCredits, true);
|
|
});
|
|
|
|
test("parseOpenrouter passes non-credits quota keys through normalizeQuotaEntry", () => {
|
|
const rows = parseOpenrouter({
|
|
quotas: {
|
|
"anthropic/claude-3.5-sonnet": {
|
|
used: 12,
|
|
total: 100,
|
|
remaining: 88,
|
|
resetAt: null,
|
|
},
|
|
},
|
|
});
|
|
assert.equal(rows.length, 1);
|
|
const row = rows[0];
|
|
assert.equal(row.name, "anthropic/claude-3.5-sonnet");
|
|
assert.equal(row.total, 100);
|
|
assert.equal(row.remaining, 88);
|
|
assert.notEqual(row.isCredits, true);
|
|
});
|