import { renderHook, waitFor } from '@testing-library/react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import React from 'react'; import { http, HttpResponse } from 'msw'; import { setupServer } from 'msw/node'; import { useStats } from '@/hooks/useStats'; import type { UsageStats, StatsQueryParams } from '@/types'; // Test data const mockStats: UsageStats[] = [ { id: 1, providerId: 'provider-1', modelName: 'gpt-4o', requestCount: 100, date: '2026-04-01', }, { id: 2, providerId: 'provider-1', modelName: 'gpt-4o-mini', requestCount: 50, date: '2026-04-01', }, ]; const mockFilteredStats: UsageStats[] = [ { id: 3, providerId: 'provider-2', modelName: 'claude-sonnet-4-5', requestCount: 200, date: '2026-04-01', }, ]; // Track the request URL for assertions let capturedUrl: URL | null = null; // MSW handlers const handlers = [ http.get('/api/stats', ({ request }) => { capturedUrl = new URL(request.url); const providerId = capturedUrl.searchParams.get('provider_id'); if (providerId === 'provider-2') { return HttpResponse.json(mockFilteredStats); } return HttpResponse.json(mockStats); }), ]; const server = setupServer(...handlers); function createTestQueryClient() { return new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false }, }, }); } function createWrapper() { const testQueryClient = createTestQueryClient(); return function Wrapper({ children }: { children: React.ReactNode }) { return ( {children} ); }; } beforeAll(() => server.listen()); afterEach(() => { server.resetHandlers(); capturedUrl = null; }); afterAll(() => server.close()); describe('useStats', () => { it('fetches stats without params', async () => { const { result } = renderHook(() => useStats(), { wrapper: createWrapper(), }); await waitFor(() => expect(result.current.isSuccess).toBe(true)); expect(result.current.data).toEqual(mockStats); expect(result.current.data).toHaveLength(2); expect(result.current.data![0]!.modelName).toBe('gpt-4o'); expect(result.current.data![1]!.requestCount).toBe(50); // Verify no query params were sent expect(capturedUrl!.search).toBe(''); }); it('with filter params passes them correctly', async () => { const params: StatsQueryParams = { providerId: 'provider-2', modelName: 'claude-sonnet-4-5', startDate: '2026-04-01', endDate: '2026-04-15', }; const { result } = renderHook(() => useStats(params), { wrapper: createWrapper(), }); await waitFor(() => expect(result.current.isSuccess).toBe(true)); expect(result.current.data).toEqual(mockFilteredStats); expect(result.current.data).toHaveLength(1); expect(result.current.data![0]!.modelName).toBe('claude-sonnet-4-5'); // Verify query params were passed correctly (snake_case) expect(capturedUrl!.searchParams.get('provider_id')).toBe('provider-2'); expect(capturedUrl!.searchParams.get('model_name')).toBe('claude-sonnet-4-5'); expect(capturedUrl!.searchParams.get('start_date')).toBe('2026-04-01'); expect(capturedUrl!.searchParams.get('end_date')).toBe('2026-04-15'); }); it('with partial filter params only sends provided params', async () => { const params: StatsQueryParams = { providerId: 'provider-1', }; const { result } = renderHook(() => useStats(params), { wrapper: createWrapper(), }); await waitFor(() => expect(result.current.isSuccess).toBe(true)); // Verify only provider_id was sent expect(capturedUrl!.searchParams.get('provider_id')).toBe('provider-1'); expect(capturedUrl!.searchParams.get('model_name')).toBeNull(); expect(capturedUrl!.searchParams.get('start_date')).toBeNull(); expect(capturedUrl!.searchParams.get('end_date')).toBeNull(); }); });