import { readFileSync } from "node:fs";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
import { createUsageState, type FetchLike, httpRequest, runUsageFeed, type UsageCredential } from "../src/index.js";
import { DEEPSEEK_USAGE_URL, deepseekUsageFeed, parseDeepSeekUsage } from "../src/utils/usage/providers/deepseek.js";
import {
OPENCODE_GO_USAGE_URL,
opencodegoUsageFeed,
parseOpenCodeGoUsage,
} from "../src/utils/usage/providers/opencode-go.js";
import { parseZaiUsage, ZAI_USAGE_URL, zaiUsageFeed } from "../src/utils/usage/providers/zai.js";
const fixturesDir = resolve(
dirname(fileURLToPath(import.meta.url)),
"..",
"..",
"..",
"native",
"usage",
"tests",
"fixtures",
);
const fixture = (name: string): unknown => JSON.parse(readFileSync(join(fixturesDir, name), "utf8"));
function scriptedFetch(responses: { status: number; body: string }[]): FetchLike {
const queue = [...responses];
return async () => {
const next = queue.shift();
if (!next) throw new Error("scripted fetch ran out of responses");
return new Response(next.body, { status: next.status });
};
}
describe("opencode-go usage feed", () => {
const credential: UsageCredential = { kind: "apiKey", apiKey: "test-opencode-key" };
it("issues a GET request with Bearer authorization and User-Agent", () => {
const state = createUsageState(credential, 0);
const step = opencodegoUsageFeed.advance(state, []);
if (!("requests" in step)) throw new Error("expected requests");
expect(step.requests).toHaveLength(1);
const req = httpRequest(step.requests[0]!);
expect(req.method).toBe("GET");
expect(req.url).toBe(OPENCODE_GO_USAGE_URL);
expect(req.headers?.Authorization).toBe("Bearer test-opencode-key");
expect(req.headers?.Accept).toBe("application/json");
expect(req.headers?.["User-Agent"]).toBe("OpenCode-Status-Plugin/1.0");
});
it("parses the recorded live fixture across 5h, 7d, and 30d windows", () => {
const body = fixture("opencode-go-usage.json");
const state = createUsageState(credential, 0);
const step = opencodegoUsageFeed.advance(state, [{ status: 200, body: JSON.stringify(body) }]);
if (!("report" in step)) throw new Error("expected report");
expect(step.report.planType).toBe("Go");
expect(step.report.error).toBeNull();
expect(step.report.notes).toEqual(["OpenCode Go unified models"]);
expect(step.report.limits).toHaveLength(3);
const [session, weekly, monthly] = step.report.limits;
expect(session).toEqual({
label: "Session",
windowLabel: "5h window",
usedPercent: 0,
resetsAt: Date.parse("2026-09-16T04:40:14.037Z"),
status: "ok",
});
expect(weekly).toEqual({
label: "Weekly",
windowLabel: "7d window",
usedPercent: 0,
resetsAt: Date.parse("2026-09-21T00:00:00.037Z"),
status: "ok",
});
expect(monthly).toEqual({
label: "Monthly",
windowLabel: "30d window",
usedPercent: 100,
resetsAt: Date.parse("2026-09-29T07:01:56.037Z"),
status: "exhausted",
});
});
it("treats rate-limited status as exhausted even under 100 percent", () => {
const parsed = parseOpenCodeGoUsage({
usage: {
rolling: { status: "rate-limited", percent: 42, resetsAt: "2026-09-16T04:40:14.037Z" },
},
});
expect(parsed.limits).toHaveLength(1);
expect(parsed.limits[0]!.status).toBe("exhausted");
});
it("maps warnings at or above 90 percent", () => {
const parsed = parseOpenCodeGoUsage({
usage: {
rolling: { status: "ok", percent: 92, resetsAt: "2026-09-16T04:40:14.037Z" },
},
});
expect(parsed.limits[0]!.status).toBe("warning");
});
it("rejects non-apiKey credential kinds", () => {
const oauthCred: UsageCredential = { kind: "oauth", access: "token" };
const step = opencodegoUsageFeed.advance(createUsageState(oauthCred, 0), []);
if (!("report" in step)) throw new Error("expected report");
expect(step.report.error).toContain("apiKey");
});
it("handles non-200 responses as error reports", () => {
const state = createUsageState(credential, 0);
const step = opencodegoUsageFeed.advance(state, [{ status: 401, body: "Unauthorized" }]);
if (!("report" in step)) throw new Error("expected report");
expect(step.report.error).toBe("HTTP 401");
});
it("runs successfully through runUsageFeed with scriptedFetch", async () => {
const report = await runUsageFeed(opencodegoUsageFeed, credential, {
fetchImpl: scriptedFetch([{ status: 200, body: JSON.stringify(fixture("opencode-go-usage.json")) }]),
});
expect(report.provider).toBe("opencode-go");
expect(report.providerName).toBe("OpenCode Zen");
expect(report.error).toBeNull();
expect(report.limits).toHaveLength(3);
});
});
describe("zai usage feed", () => {
const credential: UsageCredential = { kind: "apiKey", apiKey: "test-zai-key" };
it("issues a GET request with raw Authorization and User-Agent", () => {
const state = createUsageState(credential, 0);
const step = zaiUsageFeed.advance(state, []);
if (!("requests" in step)) throw new Error("expected requests");
expect(step.requests).toHaveLength(1);
const req = httpRequest(step.requests[0]!);
expect(req.method).toBe("GET");
expect(req.url).toBe(ZAI_USAGE_URL);
expect(req.headers?.Authorization).toBe("test-zai-key");
expect(req.headers?.["User-Agent"]).toBe("OpenCode-Status-Plugin/1.0");
});
it("parses the recorded live fixture for requests and token quota", () => {
const body = fixture("zai-usage.json");
const state = createUsageState(credential, 0);
const step = zaiUsageFeed.advance(state, [{ status: 200, body: JSON.stringify(body) }]);
if (!("report" in step)) throw new Error("expected report");
expect(step.report.planType).toBe("Lite");
expect(step.report.error).toBeNull();
expect(step.report.limits).toHaveLength(2);
const [requests, tokens] = step.report.limits;
expect(requests).toEqual({
label: "Requests",
usedPercent: 0,
resetsAt: 1792027143998,
detail: "0 / 100 reqs",
status: "ok",
});
expect(tokens).toEqual({
label: "Tokens",
windowLabel: "5h window",
usedPercent: 0,
status: "ok",
});
});
it("computes usedPercent from currentValue and usage/number when percentage is missing", () => {
const parsed = parseZaiUsage({
data: {
limits: [
{
type: "TIME_LIMIT",
unit: 5,
currentValue: 25,
usage: 100,
},
],
},
});
expect(parsed.limits[0]!.usedPercent).toBe(25);
expect(parsed.limits[0]!.detail).toBe("25 / 100 reqs");
expect(parsed.limits[0]!.status).toBe("ok");
});
it("converts epoch seconds (< 1e11) to epoch milliseconds", () => {
const parsed = parseZaiUsage({
data: {
limits: [
{
type: "TIME_LIMIT",
unit: 5,
percentage: 50,
nextResetTime: 1789517000,
},
],
},
});
expect(parsed.limits[0]!.resetsAt).toBe(1789517000 * 1000);
});
it("marks status as exhausted when usedPercent >= 100", () => {
const parsed = parseZaiUsage({
data: {
limits: [
{
type: "TOKENS_LIMIT",
unit: 3,
percentage: 100,
},
],
},
});
expect(parsed.limits[0]!.status).toBe("exhausted");
});
it("surfaces API-level errors as error reports", () => {
const state = createUsageState(credential, 0);
const step = zaiUsageFeed.advance(state, [
{ status: 200, body: JSON.stringify({ code: 401, msg: "Invalid token", success: false }) },
]);
if (!("report" in step)) throw new Error("expected report");
expect(step.report.error).toBe("Invalid token");
});
it("runs successfully through runUsageFeed with scriptedFetch", async () => {
const report = await runUsageFeed(zaiUsageFeed, credential, {
fetchImpl: scriptedFetch([{ status: 200, body: JSON.stringify(fixture("zai-usage.json")) }]),
});
expect(report.provider).toBe("zai");
expect(report.providerName).toBe("Z.ai");
expect(report.error).toBeNull();
expect(report.limits).toHaveLength(2);
});
});
describe("deepseek usage feed", () => {
const credential: UsageCredential = { kind: "apiKey", apiKey: "test-deepseek-key" };
it("issues a GET request with Bearer authorization", () => {
const state = createUsageState(credential, 0);
const step = deepseekUsageFeed.advance(state, []);
if (!("requests" in step)) throw new Error("expected requests");
expect(step.requests).toHaveLength(1);
const req = httpRequest(step.requests[0]!);
expect(req.method).toBe("GET");
expect(req.url).toBe(DEEPSEEK_USAGE_URL);
expect(req.headers?.Authorization).toBe("Bearer test-deepseek-key");
expect(req.headers?.Accept).toBe("application/json");
});
it("parses the recorded live fixture as a balance-only report", () => {
const body = fixture("deepseek-balance.json");
const state = createUsageState(credential, 0);
const step = deepseekUsageFeed.advance(state, [{ status: 200, body: JSON.stringify(body) }]);
if (!("report" in step)) throw new Error("expected report");
expect(step.report.planType).toBe("Pay-as-you-go");
expect(step.report.limits).toEqual([]);
expect(step.report.balance).toBe("$0.03 USD");
expect(step.report.error).toBeNull();
});
it("formats CNY balances with currency symbol", () => {
const parsed = parseDeepSeekUsage({
is_available: true,
balance_infos: [{ currency: "CNY", total_balance: "15.50" }],
});
expect(parsed.balance).toBe("¥15.50 CNY");
expect(parsed.limits).toEqual([]);
});
it("notes when the account is unavailable", () => {
const parsed = parseDeepSeekUsage({
is_available: false,
balance_infos: [{ currency: "USD", total_balance: "0.00" }],
});
expect(parsed.notes).toEqual(["Account is unavailable"]);
});
it("reports an error when balance_infos is empty", () => {
const state = createUsageState(credential, 0);
const step = deepseekUsageFeed.advance(state, [
{ status: 200, body: JSON.stringify({ is_available: true, balance_infos: [] }) },
]);
if (!("report" in step)) throw new Error("expected report");
expect(step.report.error).toBe("response missing balance_infos");
});
it("runs successfully through runUsageFeed with scriptedFetch", async () => {
const report = await runUsageFeed(deepseekUsageFeed, credential, {
fetchImpl: scriptedFetch([{ status: 200, body: JSON.stringify(fixture("deepseek-balance.json")) }]),
});
expect(report.provider).toBe("deepseek");
expect(report.providerName).toBe("DeepSeek");
expect(report.error).toBeNull();
expect(report.balance).toBe("$0.03 USD");
expect(report.limits).toEqual([]);
});
});