mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 09:38:39 +00:00
test: extract provider usage load coverage
This commit is contained in:
136
src/infra/provider-usage.load.test.ts
Normal file
136
src/infra/provider-usage.load.test.ts
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
import { createProviderUsageFetch, makeResponse } from "../test-utils/provider-usage-fetch.js";
|
||||||
|
import { loadProviderUsageSummary } from "./provider-usage.load.js";
|
||||||
|
import { ignoredErrors } from "./provider-usage.shared.js";
|
||||||
|
|
||||||
|
const usageNow = Date.UTC(2026, 0, 7, 0, 0, 0);
|
||||||
|
|
||||||
|
type ProviderAuth = NonNullable<
|
||||||
|
NonNullable<Parameters<typeof loadProviderUsageSummary>[0]>["auth"]
|
||||||
|
>[number];
|
||||||
|
|
||||||
|
async function loadUsageWithAuth(
|
||||||
|
auth: ProviderAuth[],
|
||||||
|
mockFetch: ReturnType<typeof createProviderUsageFetch>,
|
||||||
|
) {
|
||||||
|
return await loadProviderUsageSummary({
|
||||||
|
now: usageNow,
|
||||||
|
auth,
|
||||||
|
fetch: mockFetch as unknown as typeof fetch,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("provider-usage.load", () => {
|
||||||
|
it("loads snapshots for copilot gemini codex and xiaomi", async () => {
|
||||||
|
const mockFetch = createProviderUsageFetch(async (url) => {
|
||||||
|
if (url.includes("api.github.com/copilot_internal/user")) {
|
||||||
|
return makeResponse(200, {
|
||||||
|
quota_snapshots: { chat: { percent_remaining: 80 } },
|
||||||
|
copilot_plan: "Copilot Pro",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (url.includes("cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels")) {
|
||||||
|
return makeResponse(200, {
|
||||||
|
models: {
|
||||||
|
"gemini-2.5-pro": {
|
||||||
|
quotaInfo: { remainingFraction: 0.4, resetTime: "2026-01-08T01:00:00Z" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (url.includes("cloudcode-pa.googleapis.com/v1internal:retrieveUserQuota")) {
|
||||||
|
return makeResponse(200, {
|
||||||
|
buckets: [{ modelId: "gemini-2.5-pro", remainingFraction: 0.6 }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (url.includes("chatgpt.com/backend-api/wham/usage")) {
|
||||||
|
return makeResponse(200, {
|
||||||
|
rate_limit: { primary_window: { used_percent: 12, limit_window_seconds: 10800 } },
|
||||||
|
plan_type: "Plus",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return makeResponse(404, "not found");
|
||||||
|
});
|
||||||
|
|
||||||
|
const summary = await loadUsageWithAuth(
|
||||||
|
[
|
||||||
|
{ provider: "github-copilot", token: "copilot-token" },
|
||||||
|
{ provider: "google-gemini-cli", token: "gemini-token" },
|
||||||
|
{ provider: "openai-codex", token: "codex-token", accountId: "acc-1" },
|
||||||
|
{ provider: "xiaomi", token: "xiaomi-token" },
|
||||||
|
],
|
||||||
|
mockFetch,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(summary.providers.map((provider) => provider.provider)).toEqual([
|
||||||
|
"github-copilot",
|
||||||
|
"google-gemini-cli",
|
||||||
|
"openai-codex",
|
||||||
|
"xiaomi",
|
||||||
|
]);
|
||||||
|
expect(
|
||||||
|
summary.providers.find((provider) => provider.provider === "github-copilot")?.windows,
|
||||||
|
).toEqual([{ label: "Chat", usedPercent: 20 }]);
|
||||||
|
expect(
|
||||||
|
summary.providers.find((provider) => provider.provider === "google-gemini-cli")?.windows[0]
|
||||||
|
?.label,
|
||||||
|
).toBe("Pro");
|
||||||
|
expect(
|
||||||
|
summary.providers.find((provider) => provider.provider === "openai-codex")?.windows[0]?.label,
|
||||||
|
).toBe("3h");
|
||||||
|
expect(summary.providers.find((provider) => provider.provider === "xiaomi")?.windows).toEqual(
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns empty provider list when auth resolves to none", async () => {
|
||||||
|
const mockFetch = createProviderUsageFetch(async () => makeResponse(404, "not found"));
|
||||||
|
const summary = await loadUsageWithAuth([], mockFetch);
|
||||||
|
expect(summary).toEqual({ updatedAt: usageNow, providers: [] });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns unsupported provider snapshots for unknown provider ids", async () => {
|
||||||
|
const mockFetch = createProviderUsageFetch(async () => makeResponse(404, "not found"));
|
||||||
|
const summary = await loadUsageWithAuth(
|
||||||
|
[{ provider: "unsupported-provider", token: "token-u" }] as unknown as ProviderAuth[],
|
||||||
|
mockFetch,
|
||||||
|
);
|
||||||
|
expect(summary.providers).toHaveLength(1);
|
||||||
|
expect(summary.providers[0]?.error).toBe("Unsupported provider");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("filters errors that are marked as ignored", async () => {
|
||||||
|
const mockFetch = createProviderUsageFetch(async (url) => {
|
||||||
|
if (url.includes("api.anthropic.com/api/oauth/usage")) {
|
||||||
|
return makeResponse(500, "boom");
|
||||||
|
}
|
||||||
|
return makeResponse(404, "not found");
|
||||||
|
});
|
||||||
|
ignoredErrors.add("HTTP 500");
|
||||||
|
try {
|
||||||
|
const summary = await loadUsageWithAuth(
|
||||||
|
[{ provider: "anthropic", token: "token-a" }],
|
||||||
|
mockFetch,
|
||||||
|
);
|
||||||
|
expect(summary.providers).toEqual([]);
|
||||||
|
} finally {
|
||||||
|
ignoredErrors.delete("HTTP 500");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws when fetch is unavailable", async () => {
|
||||||
|
const previousFetch = globalThis.fetch;
|
||||||
|
vi.stubGlobal("fetch", undefined);
|
||||||
|
try {
|
||||||
|
await expect(
|
||||||
|
loadProviderUsageSummary({
|
||||||
|
now: usageNow,
|
||||||
|
auth: [{ provider: "xiaomi", token: "token-x" }],
|
||||||
|
fetch: undefined,
|
||||||
|
}),
|
||||||
|
).rejects.toThrow("fetch is not available");
|
||||||
|
} finally {
|
||||||
|
vi.stubGlobal("fetch", previousFetch);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { withTempHome } from "../../test/helpers/temp-home.js";
|
import { withTempHome } from "../../test/helpers/temp-home.js";
|
||||||
import { ensureAuthProfileStore, listProfilesForProvider } from "../agents/auth-profiles.js";
|
import { ensureAuthProfileStore, listProfilesForProvider } from "../agents/auth-profiles.js";
|
||||||
import { withEnvAsync } from "../test-utils/env.js";
|
import { withEnvAsync } from "../test-utils/env.js";
|
||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
loadProviderUsageSummary,
|
loadProviderUsageSummary,
|
||||||
type UsageSummary,
|
type UsageSummary,
|
||||||
} from "./provider-usage.js";
|
} from "./provider-usage.js";
|
||||||
import { ignoredErrors } from "./provider-usage.shared.js";
|
|
||||||
|
|
||||||
const minimaxRemainsEndpoint = "api.minimaxi.com/v1/api/openplatform/coding_plan/remains";
|
const minimaxRemainsEndpoint = "api.minimaxi.com/v1/api/openplatform/coding_plan/remains";
|
||||||
const usageNow = Date.UTC(2026, 0, 7, 0, 0, 0);
|
const usageNow = Date.UTC(2026, 0, 7, 0, 0, 0);
|
||||||
@@ -354,117 +353,4 @@ describe("provider usage loading", () => {
|
|||||||
expect(claude?.windows.some((w) => w.label === "Week")).toBe(true);
|
expect(claude?.windows.some((w) => w.label === "Week")).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("loads snapshots for copilot gemini codex and xiaomi", async () => {
|
|
||||||
const mockFetch = createProviderUsageFetch(async (url) => {
|
|
||||||
if (url.includes("api.github.com/copilot_internal/user")) {
|
|
||||||
return makeResponse(200, {
|
|
||||||
quota_snapshots: { chat: { percent_remaining: 80 } },
|
|
||||||
copilot_plan: "Copilot Pro",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (url.includes("cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels")) {
|
|
||||||
return makeResponse(200, {
|
|
||||||
models: {
|
|
||||||
"gemini-2.5-pro": {
|
|
||||||
quotaInfo: { remainingFraction: 0.4, resetTime: "2026-01-08T01:00:00Z" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (url.includes("cloudcode-pa.googleapis.com/v1internal:retrieveUserQuota")) {
|
|
||||||
return makeResponse(200, {
|
|
||||||
buckets: [{ modelId: "gemini-2.5-pro", remainingFraction: 0.6 }],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (url.includes("chatgpt.com/backend-api/wham/usage")) {
|
|
||||||
return makeResponse(200, {
|
|
||||||
rate_limit: { primary_window: { used_percent: 12, limit_window_seconds: 10800 } },
|
|
||||||
plan_type: "Plus",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return makeResponse(404, "not found");
|
|
||||||
});
|
|
||||||
|
|
||||||
const summary = await loadUsageWithAuth(
|
|
||||||
[
|
|
||||||
{ provider: "github-copilot", token: "copilot-token" },
|
|
||||||
{ provider: "google-gemini-cli", token: "gemini-token" },
|
|
||||||
{ provider: "openai-codex", token: "codex-token", accountId: "acc-1" },
|
|
||||||
{ provider: "xiaomi", token: "xiaomi-token" },
|
|
||||||
],
|
|
||||||
mockFetch,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(summary.providers.map((provider) => provider.provider)).toEqual([
|
|
||||||
"github-copilot",
|
|
||||||
"google-gemini-cli",
|
|
||||||
"openai-codex",
|
|
||||||
"xiaomi",
|
|
||||||
]);
|
|
||||||
expect(
|
|
||||||
summary.providers.find((provider) => provider.provider === "github-copilot")?.windows,
|
|
||||||
).toEqual([{ label: "Chat", usedPercent: 20 }]);
|
|
||||||
expect(
|
|
||||||
summary.providers.find((provider) => provider.provider === "google-gemini-cli")?.windows[0]
|
|
||||||
?.label,
|
|
||||||
).toBe("Pro");
|
|
||||||
expect(
|
|
||||||
summary.providers.find((provider) => provider.provider === "openai-codex")?.windows[0]?.label,
|
|
||||||
).toBe("3h");
|
|
||||||
expect(summary.providers.find((provider) => provider.provider === "xiaomi")?.windows).toEqual(
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns empty provider list when auth resolves to none", async () => {
|
|
||||||
const mockFetch = createProviderUsageFetch(async () => makeResponse(404, "not found"));
|
|
||||||
const summary = await loadUsageWithAuth([], mockFetch);
|
|
||||||
expect(summary).toEqual({ updatedAt: usageNow, providers: [] });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns unsupported provider snapshots for unknown provider ids", async () => {
|
|
||||||
const mockFetch = createProviderUsageFetch(async () => makeResponse(404, "not found"));
|
|
||||||
const summary = await loadUsageWithAuth(
|
|
||||||
[{ provider: "unsupported-provider", token: "token-u" }] as unknown as ProviderAuth[],
|
|
||||||
mockFetch,
|
|
||||||
);
|
|
||||||
expect(summary.providers).toHaveLength(1);
|
|
||||||
expect(summary.providers[0]?.error).toBe("Unsupported provider");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("filters errors that are marked as ignored", async () => {
|
|
||||||
const mockFetch = createProviderUsageFetch(async (url) => {
|
|
||||||
if (url.includes("api.anthropic.com/api/oauth/usage")) {
|
|
||||||
return makeResponse(500, "boom");
|
|
||||||
}
|
|
||||||
return makeResponse(404, "not found");
|
|
||||||
});
|
|
||||||
ignoredErrors.add("HTTP 500");
|
|
||||||
try {
|
|
||||||
const summary = await loadUsageWithAuth(
|
|
||||||
[{ provider: "anthropic", token: "token-a" }],
|
|
||||||
mockFetch,
|
|
||||||
);
|
|
||||||
expect(summary.providers).toEqual([]);
|
|
||||||
} finally {
|
|
||||||
ignoredErrors.delete("HTTP 500");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("throws when fetch is unavailable", async () => {
|
|
||||||
const previousFetch = globalThis.fetch;
|
|
||||||
vi.stubGlobal("fetch", undefined);
|
|
||||||
try {
|
|
||||||
await expect(
|
|
||||||
loadProviderUsageSummary({
|
|
||||||
now: usageNow,
|
|
||||||
auth: [{ provider: "xiaomi", token: "token-x" }],
|
|
||||||
fetch: undefined,
|
|
||||||
}),
|
|
||||||
).rejects.toThrow("fetch is not available");
|
|
||||||
} finally {
|
|
||||||
vi.stubGlobal("fetch", previousFetch);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user