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:
@@ -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");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user