Fetch OAuth quota from cli-chat-proxy billing, GetGrokCreditsConfig weekly window, and settings plan label so the dashboard matches grok.com usage.
261 lines
7.6 KiB
JavaScript
261 lines
7.6 KiB
JavaScript
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
|
|
vi.mock("../../open-sse/utils/proxyFetch.js", () => ({
|
|
proxyAwareFetch: vi.fn(),
|
|
}));
|
|
|
|
import { proxyAwareFetch } from "../../open-sse/utils/proxyFetch.js";
|
|
import { getUsageForProvider } from "../../open-sse/services/usage.js";
|
|
import { parseGrokCreditsConfig } from "../../open-sse/services/usage/xai.js";
|
|
import { parseQuotaData } from "@/app/(dashboard)/dashboard/usage/components/ProviderLimits/utils.js";
|
|
|
|
function billingResponse(config) {
|
|
return new Response(JSON.stringify({ config }), {
|
|
status: 200,
|
|
headers: { "Content-Type": "application/json" },
|
|
});
|
|
}
|
|
|
|
function settingsResponse(tier = "SuperGrok") {
|
|
return new Response(JSON.stringify({ subscription_tier_display: tier }), {
|
|
status: 200,
|
|
headers: { "Content-Type": "application/json" },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Build a minimal grpc-web GetGrokCreditsConfig payload matching the live
|
|
* SuperGrok response shape: usedPercent float + start/end timestamps.
|
|
*/
|
|
function buildCreditsGrpcFrame({ usedPercent = 18, startSec = 1783673158, endSec = 1784277958 }) {
|
|
// Encode protobuf Timestamp {1: seconds}
|
|
const encodeVarint = (n) => {
|
|
const out = [];
|
|
let v = n >>> 0;
|
|
while (v >= 0x80) {
|
|
out.push((v & 0x7f) | 0x80);
|
|
v >>>= 7;
|
|
}
|
|
out.push(v);
|
|
return Buffer.from(out);
|
|
};
|
|
const encodeKey = (field, wt) => encodeVarint((field << 3) | wt);
|
|
const encodeTimestamp = (seconds) => {
|
|
const body = Buffer.concat([encodeKey(1, 0), encodeVarint(seconds)]);
|
|
return body;
|
|
};
|
|
const encodeLen = (field, bytes) =>
|
|
Buffer.concat([encodeKey(field, 2), encodeVarint(bytes.length), bytes]);
|
|
const encodeFloat = (field, f) => {
|
|
const buf = Buffer.alloc(4);
|
|
buf.writeFloatLE(f, 0);
|
|
return Buffer.concat([encodeKey(field, 5), buf]);
|
|
};
|
|
|
|
const startTs = encodeTimestamp(startSec);
|
|
const endTs = encodeTimestamp(endSec);
|
|
const config = Buffer.concat([
|
|
encodeFloat(1, usedPercent),
|
|
encodeLen(4, startTs),
|
|
encodeLen(5, endTs),
|
|
]);
|
|
const msg = encodeLen(1, config);
|
|
const frame = Buffer.alloc(5 + msg.length);
|
|
frame[0] = 0;
|
|
frame.writeUInt32BE(msg.length, 1);
|
|
msg.copy(frame, 5);
|
|
return frame;
|
|
}
|
|
|
|
function creditsResponse(opts = {}) {
|
|
const frame = buildCreditsGrpcFrame(opts);
|
|
return new Response(frame, {
|
|
status: 200,
|
|
headers: { "Content-Type": "application/grpc-web+proto" },
|
|
});
|
|
}
|
|
|
|
function mockXaiHappyPath({ used = 733, limit = 15000, weeklyPercent = 18 } = {}) {
|
|
proxyAwareFetch.mockImplementation(async (url) => {
|
|
if (String(url).includes("/v1/billing")) {
|
|
return billingResponse({
|
|
monthlyLimit: { val: limit },
|
|
used: { val: used },
|
|
onDemandCap: { val: 0 },
|
|
billingPeriodStart: "2026-07-01T00:00:00+00:00",
|
|
billingPeriodEnd: "2026-08-01T00:00:00+00:00",
|
|
});
|
|
}
|
|
if (String(url).includes("GetGrokCreditsConfig")) {
|
|
return creditsResponse({ usedPercent: weeklyPercent });
|
|
}
|
|
if (String(url).includes("/v1/settings")) {
|
|
return settingsResponse("SuperGrok");
|
|
}
|
|
return new Response("{}", { status: 404 });
|
|
});
|
|
}
|
|
|
|
describe("xAI usage", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it("parseGrokCreditsConfig extracts weekly used% and reset timestamp", () => {
|
|
const frame = buildCreditsGrpcFrame({
|
|
usedPercent: 18,
|
|
startSec: 1783673158,
|
|
endSec: 1784277958,
|
|
});
|
|
const parsed = parseGrokCreditsConfig(frame);
|
|
expect(parsed).toMatchObject({
|
|
usedPercent: 18,
|
|
remainingPercent: 82,
|
|
});
|
|
expect(parsed.resetAt).toBe(new Date(1784277958 * 1000).toISOString());
|
|
expect(parsed.periodStart).toBe(new Date(1783673158 * 1000).toISOString());
|
|
});
|
|
|
|
it("fetches billing + weekly credits + settings in parallel", async () => {
|
|
mockXaiHappyPath();
|
|
|
|
const usage = await getUsageForProvider({
|
|
provider: "xai",
|
|
accessToken: "tok-abc",
|
|
});
|
|
|
|
const urls = proxyAwareFetch.mock.calls.map((c) => String(c[0]));
|
|
expect(urls).toEqual(
|
|
expect.arrayContaining([
|
|
"https://cli-chat-proxy.grok.com/v1/billing",
|
|
"https://grok.com/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig",
|
|
"https://cli-chat-proxy.grok.com/v1/settings",
|
|
]),
|
|
);
|
|
|
|
expect(usage.plan).toBe("SuperGrok");
|
|
expect(usage.quotas.weekly).toMatchObject({
|
|
used: 18,
|
|
total: 100,
|
|
remaining: 82,
|
|
});
|
|
expect(usage.quotas.api_usage).toMatchObject({
|
|
used: 733,
|
|
total: 15000,
|
|
unit: "credits",
|
|
});
|
|
expect(usage.quotas.api_usage.resetAt).toBe("2026-08-01T00:00:00.000Z");
|
|
});
|
|
|
|
it("still returns weekly when billing fails but credits succeed", async () => {
|
|
proxyAwareFetch.mockImplementation(async (url) => {
|
|
if (String(url).includes("/v1/billing")) {
|
|
return new Response("nope", { status: 503 });
|
|
}
|
|
if (String(url).includes("GetGrokCreditsConfig")) {
|
|
return creditsResponse({ usedPercent: 42 });
|
|
}
|
|
if (String(url).includes("/v1/settings")) {
|
|
return settingsResponse("SuperGrok");
|
|
}
|
|
return new Response("{}", { status: 404 });
|
|
});
|
|
|
|
const usage = await getUsageForProvider({
|
|
provider: "xai",
|
|
accessToken: "tok",
|
|
});
|
|
|
|
expect(usage.plan).toBe("SuperGrok");
|
|
expect(usage.quotas.weekly).toMatchObject({ used: 42, total: 100, remaining: 58 });
|
|
expect(usage.quotas.api_usage).toBeUndefined();
|
|
});
|
|
|
|
it("includes on-demand row when onDemandCap > 0", async () => {
|
|
proxyAwareFetch.mockImplementation(async (url) => {
|
|
if (String(url).includes("/v1/billing")) {
|
|
return billingResponse({
|
|
monthlyLimit: { val: 1000 },
|
|
used: { val: 100 },
|
|
onDemandCap: { val: 500 },
|
|
billingPeriodEnd: "2026-08-01T00:00:00+00:00",
|
|
});
|
|
}
|
|
if (String(url).includes("GetGrokCreditsConfig")) {
|
|
return creditsResponse({ usedPercent: 10 });
|
|
}
|
|
if (String(url).includes("/v1/settings")) {
|
|
return settingsResponse("SuperGrok");
|
|
}
|
|
return new Response("{}", { status: 404 });
|
|
});
|
|
|
|
const usage = await getUsageForProvider({
|
|
provider: "xai",
|
|
accessToken: "tok",
|
|
});
|
|
|
|
expect(usage.quotas.on_demand).toMatchObject({
|
|
used: 0,
|
|
total: 500,
|
|
remainingCredits: 500,
|
|
});
|
|
});
|
|
|
|
it("returns auth message on 401 billing", async () => {
|
|
proxyAwareFetch.mockImplementation(async (url) => {
|
|
if (String(url).includes("/v1/billing")) {
|
|
return new Response("unauthorized", { status: 401 });
|
|
}
|
|
return new Response("{}", { status: 404 });
|
|
});
|
|
|
|
const usage = await getUsageForProvider({
|
|
provider: "xai",
|
|
accessToken: "tok",
|
|
});
|
|
|
|
expect(usage.message).toMatch(/re-authorize/i);
|
|
});
|
|
|
|
it("parseQuotaData maps weekly + api_usage labels", () => {
|
|
const rows = parseQuotaData("xai", {
|
|
quotas: {
|
|
weekly: {
|
|
used: 18,
|
|
total: 100,
|
|
remaining: 82,
|
|
remainingPercentage: 82,
|
|
resetAt: "2026-07-17T08:45:58.000Z",
|
|
},
|
|
api_usage: {
|
|
used: 733,
|
|
total: 15000,
|
|
remainingCredits: 14267,
|
|
unit: "credits",
|
|
resetAt: "2026-08-01T00:00:00.000Z",
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(rows).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
name: "Weekly limit",
|
|
used: 18,
|
|
total: 100,
|
|
remaining: 82,
|
|
}),
|
|
expect.objectContaining({
|
|
name: "Api usage",
|
|
used: 733,
|
|
total: 15000,
|
|
unit: "credits",
|
|
}),
|
|
]),
|
|
);
|
|
const api = rows.find((r) => r.name === "Api usage");
|
|
expect(api.remaining).toBeUndefined();
|
|
});
|
|
});
|