fix(ui): tighten analytics session and query contracts

This commit is contained in:
Tam Nhu Tran committed 2026-04-27 12:30:56 -04:00
1 parent e95b354297
commit b2e4be310d
9 files changed
+162 -106

No files matched your search

+2
View File
@@ -11,6 +11,8 @@ import { afterEach, vi } from 'vitest';
// Cleanup after each test
afterEach(() => {
cleanup();
vi.restoreAllMocks();
vi.unstubAllGlobals();
});
// Mock matchMedia for components that use media queries
@@ -58,7 +58,7 @@ describe('SessionStatsCard', () => {
expect(screen.getByText('No session data available')).toBeInTheDocument();
});
it('uses the API total for overall session count while labeling subset averages as recent', () => {
it('keeps subset session metrics explicitly sample-scoped when pagination truncates the result set', () => {
const data = buildPaginatedSessions({
sessions: [
buildSession({ sessionId: 'session-1', cost: 0.08 }),
@@ -84,8 +84,9 @@ describe('SessionStatsCard', () => {
render(<SessionStatsCard data={data} />, { wrapper: AllProviders });
expect(screen.getByText('Total Sessions')).toBeInTheDocument();
expect(screen.getByText('9')).toBeInTheDocument();
expect(screen.getByText('Sampled Sessions')).toBeInTheDocument();
expect(screen.getByText('3')).toBeInTheDocument();
expect(screen.getByText('9 total')).toBeInTheDocument();
expect(screen.getByText('Recent Avg Cost')).toBeInTheDocument();
expect(screen.getAllByText('$0.08').length).toBeGreaterThan(0);
expect(screen.getByText('codex')).toBeInTheDocument();
@@ -10,21 +10,22 @@ function jsonResponse(body: unknown) {
describe('analytics usage API contract', () => {
beforeEach(() => {
global.fetch = vi
.fn()
.mockImplementation(() => Promise.resolve(jsonResponse({ data: {} }))) as typeof fetch;
vi.stubGlobal(
'fetch',
vi.fn().mockImplementation(() => Promise.resolve(jsonResponse({ data: {} }))) as typeof fetch
);
});
it('omits unsupported profile and months query params while keeping supported filters', async () => {
it('serializes only the supported analytics query params', async () => {
const startDate = new Date(2026, 3, 1);
const endDate = new Date(2026, 3, 30);
await usageApi.summary({ startDate, endDate, profile: 'work' });
await usageApi.trends({ startDate, endDate, profile: 'work' });
await usageApi.models({ startDate, endDate, profile: 'work' });
await usageApi.sessions({ startDate, endDate, profile: 'work', limit: 50, offset: 10 });
await usageApi.insights({ startDate, endDate, profile: 'work' });
await usageApi.monthly({ startDate, endDate, profile: 'work' });
await usageApi.summary({ startDate, endDate });
await usageApi.trends({ startDate, endDate });
await usageApi.models({ startDate, endDate });
await usageApi.sessions({ startDate, endDate, limit: 50, offset: 10 });
await usageApi.insights({ startDate, endDate });
await usageApi.monthly({ startDate, endDate });
const urls = vi.mocked(global.fetch).mock.calls.map(([url]) => String(url));
@@ -34,7 +35,6 @@ describe('analytics usage API contract', () => {
expect(urls).toContain('/api/usage/sessions?since=20260401&until=20260430&limit=50&offset=10');
expect(urls).toContain('/api/usage/insights?since=20260401&until=20260430');
expect(urls).toContain('/api/usage/monthly?since=20260401&until=20260430');
expect(urls.every((url) => !url.includes('profile='))).toBe(true);
expect(urls.every((url) => !url.includes('months='))).toBe(true);
});
});