import { beforeAll, describe, expect, it, vi } from "bun:test"; import type { UsageReport } from "@oh-my-pi/pi-ai"; import { CommandController } from "@oh-my-pi/pi-coding-agent/modes/controllers/command-controller"; import { getThemeByName, setThemeInstance } from "@oh-my-pi/pi-coding-agent/modes/theme/theme"; import type { InteractiveModeContext } from "@oh-my-pi/pi-coding-agent/modes/types"; interface RenderableBlock { render(width: number): string[]; } function isRenderableBlock(value: unknown): value is RenderableBlock { return value !== null && typeof value === "object" && "render" in value && typeof value.render === "function"; } function renderPresentedBlocks(value: unknown): string { const blocks = Array.isArray(value) ? value : [value]; return blocks .filter(isRenderableBlock) .flatMap(block => block.render(120)) .join("\n"); } describe("CommandController /usage", () => { beforeAll(async () => { const theme = await getThemeByName("dark"); if (!theme) throw new Error("Expected dark theme"); setThemeInstance(theme); }); it("renders bars and free percentage for limits that only report remainingFraction", async () => { const present = vi.fn(); const ctx = { session: {}, ui: { terminal: { columns: 100 } }, present, showWarning: vi.fn(), showError: vi.fn(), } as unknown as InteractiveModeContext; const controller = new CommandController(ctx); const reports: UsageReport[] = [ { provider: "openai-codex", fetchedAt: 1_700_000_000_000, limits: [ { id: "codex-weekly", label: "Weekly", scope: { provider: "openai-codex", tier: "pro", accountId: "acct-1" }, window: { id: "weekly", label: "weekly" }, amount: { remainingFraction: 0.25, unit: "requests" }, status: "ok", }, ], metadata: { email: "user@example.com" }, }, ]; await controller.handleUsageCommand(reports); expect(present).toHaveBeenCalledTimes(1); const firstCall = present.mock.calls[0]; expect(firstCall).toBeDefined(); const output = renderPresentedBlocks(firstCall?.[0]); expect(output).toContain("25% free"); expect(output).toContain("█"); expect(output).not.toContain("··········"); }); it("renders saved reset expiry lines for future and expired credits", async () => { const present = vi.fn(); const ctx = { session: {}, ui: { terminal: { columns: 100 } }, present, showWarning: vi.fn(), showError: vi.fn(), } as unknown as InteractiveModeContext; const controller = new CommandController(ctx); const now = Date.now(); const dayMs = 24 * 60 * 60 * 1000; const futureIso = new Date(now + 2 * dayMs).toISOString(); const expiredIso = new Date(now - 2 * dayMs).toISOString(); const reports: UsageReport[] = [ { provider: "openai-codex", fetchedAt: now, limits: [], metadata: { email: "user@example.com" }, resetCredits: { availableCount: 2, credits: [{ expiresAt: futureIso }, { expiresAt: expiredIso }], }, }, ]; await controller.handleUsageCommand(reports); expect(present).toHaveBeenCalledTimes(1); const firstCall = present.mock.calls[0]; expect(firstCall).toBeDefined(); const output = renderPresentedBlocks(firstCall?.[0]); expect(output).toContain("Saved rate-limit resets"); expect(output).toContain("user@example.com: 2 saved resets"); expect(output).toContain(`expires in`); expect(output).toContain(`(${futureIso.slice(0, 10)})`); expect(output).toContain(`expired (${expiredIso.slice(0, 10)})`); }); });