test(KB-215): add pace integration tests for usage indicators

- Add backend tests verifying pace is attached to windows with valid timing data

- Add frontend test verifying UsageIndicator displays pace from backend data

- Test full data flow from backend calculation to UI display
This commit is contained in:
gsxdsm
2026-03-30 17:18:18 -07:00
parent 93f0cb75b4
commit bbbb668044
2 changed files with 129 additions and 0 deletions

View File

@@ -881,4 +881,49 @@ describe("UsageIndicator", () => {
paceRow = screen.getByTestId("pace-row");
expect(paceRow).toHaveTextContent("Using 20% over pace");
});
it("verifies pace appears for weekly windows with valid backend timing data", () => {
// This test verifies the full data flow: backend calculates pace when both
// resetMs and windowDurationMs are present, frontend displays the indicator
mockUseUsageData.mockReturnValue({
providers: [
{
name: "TestProvider",
icon: "🧪",
status: "ok",
windows: [
{
label: "Weekly",
percentUsed: 40,
percentLeft: 60,
resetText: "resets in 4d",
resetMs: 345600000, // 4 days remaining
windowDurationMs: 604800000, // 7 days total
// Pace should be calculated by backend and included in response
pace: {
status: "behind",
percentElapsed: 43,
message: "Using 3% under pace",
},
},
],
},
],
loading: false,
error: null,
lastUpdated: new Date(),
refresh: mockRefresh,
});
render(<UsageIndicator isOpen={true} onClose={mockOnClose} />);
// Pace marker should be rendered
const paceMarker = document.querySelector('[data-testid="pace-marker"]');
expect(paceMarker).toBeInTheDocument();
// Pace row should show status
const paceRow = screen.getByTestId("pace-row");
expect(paceRow).toBeInTheDocument();
expect(paceRow).toHaveTextContent("under pace");
});
});

View File

@@ -981,4 +981,88 @@ describe("usage", () => {
expect(codex.windows[0].resetText).toContain("1h 1m");
});
});
describe("pace integration with provider windows", () => {
it("attaches pace to Minimax weekly window with valid timing data", async () => {
const mockResponse = {
quota: {
total: 1000,
used: 350,
remaining: 650,
},
reset_at: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000).toISOString(),
};
mockReadFileSync.mockImplementation((path: string) => {
if (path.includes("minimax")) {
return JSON.stringify({ access_token: "test-token" });
}
throw new Error("File not found");
});
const mockReq = { on: vi.fn(), write: vi.fn(), end: vi.fn() };
mockRequest.mockImplementation((options: any, callback: any) => {
const mockRes = {
statusCode: 200,
headers: {},
on: vi.fn((event: string, handler: any) => {
if (event === "data") handler(Buffer.from(JSON.stringify(mockResponse)));
if (event === "end") handler();
}),
};
callback(mockRes);
return mockReq;
});
const providers = await fetchAllProviderUsage();
const minimax = providers.find((p) => p.name === "Minimax")!;
expect(minimax.status).toBe("ok");
expect(minimax.windows).toHaveLength(1);
const weeklyWindow = minimax.windows[0];
expect(weeklyWindow.label).toBe("Weekly");
expect(weeklyWindow.pace).toBeDefined();
expect(weeklyWindow.pace!.status).toBe("behind");
expect(weeklyWindow.pace!.percentElapsed).toBeGreaterThan(0);
expect(weeklyWindow.pace!.message).toContain("under pace");
});
it("does not attach pace when resetMs is 0 (window already reset)", async () => {
const mockResponse = {
quota: { total: 1000, used: 0, remaining: 1000 },
reset_at: new Date(Date.now() - 1000).toISOString(),
};
mockReadFileSync.mockImplementation((path: string) => {
if (path.includes("minimax")) {
return JSON.stringify({ access_token: "test-token" });
}
throw new Error("File not found");
});
const mockReq = { on: vi.fn(), write: vi.fn(), end: vi.fn() };
mockRequest.mockImplementation((options: any, callback: any) => {
const mockRes = {
statusCode: 200,
headers: {},
on: vi.fn((event: string, handler: any) => {
if (event === "data") handler(Buffer.from(JSON.stringify(mockResponse)));
if (event === "end") handler();
}),
};
callback(mockRes);
return mockReq;
});
const providers = await fetchAllProviderUsage();
const minimax = providers.find((p) => p.name === "Minimax")!;
expect(minimax.status).toBe("ok");
expect(minimax.windows[0].resetMs).toBe(0);
expect(minimax.windows[0].pace).toBeUndefined();
});
});
});