openclaw/extensions/xai/usage.test.ts
Bobby Bones 7f0ea8e546
fix: SuperGrok usage shows No usage data when xAI omits creditUsagePercent (#155790)
<!--
Related: #155782

Required PR title:
fix: SuperGrok usage shows No usage data when xAI omits creditUsagePercent
-->

Related: #155782

## What Problem This Solves

Fixes: SuperGrok usage cards and `openclaw status --usage` show "No usage data" when xAI returns a valid weekly billing period that omits `creditUsagePercent`.

## User Impact

User impact: OAuth SuperGrok accounts now get a specific "Included usage omitted" snapshot, plus plan and prepaid facts, instead of a generic missing-telemetry error. Grok inference was already working; only quota display was wrong.

## Why This Change Was Made

xAI can omit a default-zero included-usage scalar on an otherwise valid weekly or monthly billing period. The parser now treats that as omitted included usage rather than inventing a percentage or reading on-demand pay-as-you-go counters as SuperGrok subscription quota. Legacy monthly `used` / `monthlyLimit` parsing and explicit `creditUsagePercent: 0` windows stay unchanged.

## Evidence

Exact head: `19aae5939ec429b30333e8db836bf6276d583895`. No code change in this proof pass.

Live usage-only billing probe against `GET https://cli-chat-proxy.grok.com/v1/billing?format=credits` (HTTP 200, 413 bytes). Isolated disposable Gateway on `127.0.0.1:63012` (`openclaw gateway run --dev`). Live operator Gateway on `127.0.0.1:18789` (pid 22298) was not restarted, modified, or used for this proof. No inference requests.

Sanitized live billing payload: valid weekly period, `creditUsagePercent` omitted, no legacy `used` / `monthlyLimit`:

```json
{
  "config": {
    "currentPeriod": {
      "type": "USAGE_PERIOD_TYPE_WEEKLY",
      "start": "2026-09-21T12:01:29.688516+00:00",
      "end": "2026-09-28T12:01:29.688516+00:00"
    },
    "onDemandCap": { "val": 0 },
    "onDemandUsed": { "val": 0 },
    "isUnifiedBillingUser": true,
    "prepaidBalance": { "val": 0 },
    "billingPeriodStart": "2026-09-21T12:01:29.688516+00:00",
    "billingPeriodEnd": "2026-09-28T12:01:29.688516+00:00"
  }
}
```

`has_creditUsagePercent`: false. Period recognized as weekly.

PR-head CLI after the same live account, isolated config/port/session store (exit 0):

```text
pnpm openclaw status --usage --timeout 30000
Usage:
  SuperGrok (SuperGrok)
    Included usage omitted
    Prepaid balance: $0.00
```

```text
pnpm openclaw models status
OAuth/token status
- xai usage: Included usage omitted
```

Before (same payload on current main / pre-fix adapter): `SuperGrok: No usage data`.

Focused tests: `node scripts/run-vitest.mjs extensions/xai/usage.test.ts --maxWorkers=1` — 12 passed. File wall 12.38s on one worker; individual cases 1–9ms.

Inspected, sanitized Provider Plans billing-card pair from exact PR-head Control UI (`19aae5939ec429b30333e8db836bf6276d583895`) with a local mocked `usage.status` renderer. Isolated Vite loopback `http://127.0.0.1:51133/`; live operator Gateway `127.0.0.1:18789` (pid 22298) was not used, restarted, or modified. No inference requests.

**Before** (pre-fix SuperGrok snapshot: `error: "No usage data"`):

![Before: SuperGrok Provider Plans card showing No usage data](https://github.com/user-attachments/assets/e315c018-edd5-4e58-99af-f6cbdebe6b66)

**After** (PR-head SuperGrok snapshot: plan + prepaid `$0.00` + `Included usage omitted`):

![After: SuperGrok Provider Plans card showing Included usage omitted](https://github.com/user-attachments/assets/b6157fb5-1cb8-4876-8e00-2b8321b29a13)

No emails, tokens, account identifiers, or private endpoints. Uploaded via the fork `user-attachments` endpoint after upstream `repository_id` returned 404 (no push on `openclaw/openclaw`).

Remaining semantic uncertainty: omitted `creditUsagePercent` is reported as omitted, not proven 0%. Cross-client reports (including [stablyai/orca#20826](https://github.com/stablyai/orca/issues/20826)) suggest xAI drops default-zero credit fields, but that is not a documented xAI contract.


Made with [Cursor](https://cursor.com)

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-23 10:06:33 +05:30

236 lines
6.9 KiB
TypeScript

// Covers SuperGrok provider usage fetch parsing.
import { createProviderUsageFetch, makeResponse } from "openclaw/plugin-sdk/test-env";
import { describe, expect, it } from "vitest";
import { fetchXaiUsage } from "./usage.js";
describe("fetchXaiUsage", () => {
it("fetches SuperGrok billing with Grok CLI headers", async () => {
const reset = "2026-09-08T12:00:00Z";
const mockFetch = createProviderUsageFetch(async (url, init) => {
expect(url).toBe("https://cli-chat-proxy.grok.com/v1/billing?format=credits");
expect(init?.headers).toMatchObject({
Authorization: "Bearer oauth-token",
Accept: "application/json",
"x-grok-client-mode": "cli",
"x-grok-client-version": "1.0.4",
});
return makeResponse(200, {
subscription_tier: "SuperGrok Heavy",
config: {
creditUsagePercent: 28.4,
currentPeriod: {
type: "SUBSCRIPTION_PERIOD_WEEKLY",
end: reset,
},
prepaidBalance: { val: "1250" },
},
});
});
await expect(fetchXaiUsage("oauth-token", 5000, mockFetch)).resolves.toEqual({
provider: "xai",
displayName: "SuperGrok",
windows: [
{
label: "Weekly",
usedPercent: 28.4,
resetAt: new Date(reset).getTime(),
},
],
billing: [
{
type: "balance",
label: "Prepaid balance",
amount: 12.5,
unit: "USD",
},
],
plan: "SuperGrok Heavy",
});
});
it("parses legacy monthly counters when the billing percent is absent", async () => {
const mockFetch = createProviderUsageFetch(async () =>
makeResponse(200, {
subscriptionTier: "Premium+",
config: {
used: { val: 2500 },
monthly_limit: { val: 10000 },
billing_period_end: "2026-09-30T00:00:00Z",
},
}),
);
const result = await fetchXaiUsage("oauth-token", 5000, mockFetch);
expect(result.windows).toEqual([
{
label: "Monthly",
usedPercent: 25,
resetAt: new Date("2026-09-30T00:00:00Z").getTime(),
},
]);
expect(result.plan).toBe("Premium+");
});
it("keeps an explicit zero included-usage percent as a weekly window", async () => {
const reset = "2026-09-28T12:01:29Z";
const mockFetch = createProviderUsageFetch(async () =>
makeResponse(200, {
config: {
creditUsagePercent: 0,
currentPeriod: {
type: "USAGE_PERIOD_TYPE_WEEKLY",
start: "2026-09-21T12:01:29Z",
end: reset,
},
},
}),
);
const result = await fetchXaiUsage("oauth-token", 5000, mockFetch);
expect(result).toMatchObject({
displayName: "SuperGrok",
plan: "SuperGrok",
windows: [
{
label: "Weekly",
usedPercent: 0,
resetAt: new Date(reset).getTime(),
},
],
});
expect(result.error).toBeUndefined();
expect(result.summary).toBeUndefined();
});
it("reports omitted included usage for a valid weekly billing period without inventing a percent", async () => {
const reset = "2026-09-28T12:01:29Z";
const mockFetch = createProviderUsageFetch(async () =>
makeResponse(200, {
config: {
currentPeriod: {
type: "USAGE_PERIOD_TYPE_WEEKLY",
start: "2026-09-21T12:01:29Z",
end: reset,
},
onDemandCap: { val: 0 },
onDemandUsed: { val: 0 },
isUnifiedBillingUser: true,
prepaidBalance: { val: 0 },
billingPeriodStart: "2026-09-21T12:01:29Z",
billingPeriodEnd: reset,
},
}),
);
const result = await fetchXaiUsage("oauth-token", 5000, mockFetch);
expect(result).toEqual({
provider: "xai",
displayName: "SuperGrok",
windows: [],
billing: [
{
type: "balance",
label: "Prepaid balance",
amount: 0,
unit: "USD",
},
],
plan: "SuperGrok",
summary: "Included usage omitted",
});
});
it("does not treat on-demand counters as included SuperGrok quota", async () => {
const mockFetch = createProviderUsageFetch(async () =>
makeResponse(200, {
config: {
currentPeriod: {
type: "USAGE_PERIOD_TYPE_WEEKLY",
start: "2026-09-21T12:01:29Z",
end: "2026-09-28T12:01:29Z",
},
onDemandCap: { val: 10000 },
onDemandUsed: { val: 2500 },
prepaidBalance: { val: 0 },
},
}),
);
const result = await fetchXaiUsage("oauth-token", 5000, mockFetch);
expect(result.windows).toEqual([]);
expect(result.summary).toBe("Included usage omitted");
expect(result.error).toBeUndefined();
expect(result.plan).toBe("SuperGrok");
});
it("reports omitted included usage for a valid monthly billing period", async () => {
const mockFetch = createProviderUsageFetch(async () =>
makeResponse(200, {
config: {
currentPeriod: {
type: "USAGE_PERIOD_TYPE_MONTHLY",
start: "2026-09-01T00:00:00Z",
end: "2026-10-01T00:00:00Z",
},
},
}),
);
const result = await fetchXaiUsage("oauth-token", 5000, mockFetch);
expect(result.windows).toEqual([]);
expect(result.summary).toBe("Included usage omitted");
expect(result.error).toBeUndefined();
expect(result.plan).toBe("SuperGrok");
});
it("returns token-expired errors for billing auth failures", async () => {
const mockFetch = createProviderUsageFetch(async () => makeResponse(401, { error: "expired" }));
const result = await fetchXaiUsage("oauth-token", 5000, mockFetch);
expect(result).toEqual({
provider: "xai",
displayName: "xAI",
windows: [],
error: "Token expired",
});
});
it.each([
["malformed JSON", "{not-json", "Malformed billing response"],
["missing config", {}, "Malformed billing response"],
["missing usage fields", { config: { prepaidBalance: { val: "100" } } }, "No usage data"],
[
"weekly period without bounds",
{ config: { currentPeriod: { type: "USAGE_PERIOD_TYPE_WEEKLY" } } },
"No usage data",
],
[
"unusable included-usage percent",
{
config: {
creditUsagePercent: -1,
currentPeriod: {
type: "USAGE_PERIOD_TYPE_WEEKLY",
start: "2026-09-21T12:01:29Z",
end: "2026-09-28T12:01:29Z",
},
},
},
"No usage data",
],
])("returns a stable error for %s", async (_name, payload, error) => {
const mockFetch = createProviderUsageFetch(async () => makeResponse(200, payload));
const result = await fetchXaiUsage("oauth-token", 5000, mockFetch);
expect(result.error).toBe(error);
expect(result.windows).toHaveLength(0);
});
});