diff --git a/src/infra/provider-usage.fetch.copilot.test.ts b/src/infra/provider-usage.fetch.copilot.test.ts index 7df17118159..0abfd5f782f 100644 --- a/src/infra/provider-usage.fetch.copilot.test.ts +++ b/src/infra/provider-usage.fetch.copilot.test.ts @@ -34,4 +34,40 @@ describe("fetchCopilotUsage", () => { { label: "Chat", usedPercent: 25 }, ]); }); + + it("defaults missing snapshot values and clamps invalid remaining percentages", async () => { + const mockFetch = createProviderUsageFetch(async () => + makeResponse(200, { + quota_snapshots: { + premium_interactions: { percent_remaining: null }, + chat: { percent_remaining: 140 }, + }, + }), + ); + + const result = await fetchCopilotUsage("token", 5000, mockFetch); + + expect(result.windows).toEqual([ + { label: "Premium", usedPercent: 100 }, + { label: "Chat", usedPercent: 0 }, + ]); + expect(result.plan).toBeUndefined(); + }); + + it("returns an empty window list when quota snapshots are missing", async () => { + const mockFetch = createProviderUsageFetch(async () => + makeResponse(200, { + copilot_plan: "free", + }), + ); + + const result = await fetchCopilotUsage("token", 5000, mockFetch); + + expect(result).toEqual({ + provider: "github-copilot", + displayName: "Copilot", + windows: [], + plan: "free", + }); + }); }); diff --git a/src/infra/provider-usage.shared.test.ts b/src/infra/provider-usage.shared.test.ts index 578cb876a4f..048352a183d 100644 --- a/src/infra/provider-usage.shared.test.ts +++ b/src/infra/provider-usage.shared.test.ts @@ -1,7 +1,12 @@ -import { describe, expect, it } from "vitest"; +import { afterEach, describe, expect, it, vi } from "vitest"; import { clampPercent, resolveUsageProviderId, withTimeout } from "./provider-usage.shared.js"; describe("provider-usage.shared", () => { + afterEach(() => { + vi.useRealTimers(); + vi.restoreAllMocks(); + }); + it.each([ { value: "z-ai", expected: "zai" }, { value: " GOOGLE-GEMINI-CLI ", expected: "google-gemini-cli" }, @@ -33,7 +38,18 @@ describe("provider-usage.shared", () => { }); it("returns fallback when timeout wins", async () => { + vi.useFakeTimers(); const late = new Promise((resolve) => setTimeout(() => resolve("late"), 50)); - await expect(withTimeout(late, 1, "fallback")).resolves.toBe("fallback"); + const result = withTimeout(late, 1, "fallback"); + await vi.advanceTimersByTimeAsync(1); + await expect(result).resolves.toBe("fallback"); + }); + + it("clears the timeout after successful work", async () => { + const clearTimeoutSpy = vi.spyOn(globalThis, "clearTimeout"); + + await expect(withTimeout(Promise.resolve("ok"), 100, "fallback")).resolves.toBe("ok"); + + expect(clearTimeoutSpy).toHaveBeenCalledTimes(1); }); });