import { QueryClientProvider } from '@tanstack/react-query' import { renderHook, waitFor, act } from '@testing-library/react' import { createTestQueryClient } from '../test/utils' import { useRuns, useRun, useCreateRun, useUpdateRun, useDeleteRun, useNamingCategories, useNameSuggestions, } from './useRuns' vi.mock('../api/runs') vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } })) import { getRuns, getRun, createRun, updateRun, deleteRun, getNamingCategories } from '../api/runs' import { toast } from 'sonner' function createWrapper() { const queryClient = createTestQueryClient() const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ) return { queryClient, wrapper } } describe('useRuns', () => { it('calls getRuns and returns data', async () => { const runs = [{ id: 1, name: 'My Run' }] vi.mocked(getRuns).mockResolvedValue(runs as never) const { wrapper } = createWrapper() const { result } = renderHook(() => useRuns(), { wrapper }) await waitFor(() => expect(result.current.isSuccess).toBe(true)) expect(getRuns).toHaveBeenCalledOnce() expect(result.current.data).toEqual(runs) }) }) describe('useRun', () => { it('calls getRun with the given id', async () => { const run = { id: 3, name: 'Specific Run' } vi.mocked(getRun).mockResolvedValue(run as never) const { wrapper } = createWrapper() const { result } = renderHook(() => useRun(3), { wrapper }) await waitFor(() => expect(result.current.isSuccess).toBe(true)) expect(getRun).toHaveBeenCalledWith(3) }) }) describe('useCreateRun', () => { it('calls createRun with the provided input', async () => { vi.mocked(createRun).mockResolvedValue({ id: 10 } as never) const { wrapper } = createWrapper() const { result } = renderHook(() => useCreateRun(), { wrapper }) await act(async () => { await result.current.mutateAsync({ name: 'New Run', gameId: 1, status: 'active' } as never) }) expect(createRun).toHaveBeenCalledWith({ name: 'New Run', gameId: 1, status: 'active' }) }) it('invalidates the runs query on success', async () => { vi.mocked(createRun).mockResolvedValue({} as never) const { queryClient, wrapper } = createWrapper() const spy = vi.spyOn(queryClient, 'invalidateQueries') const { result } = renderHook(() => useCreateRun(), { wrapper }) await act(async () => { await result.current.mutateAsync({} as never) }) expect(spy).toHaveBeenCalledWith({ queryKey: ['runs'] }) }) }) describe('useUpdateRun', () => { it('calls updateRun with the given id and data', async () => { vi.mocked(updateRun).mockResolvedValue({} as never) const { wrapper } = createWrapper() const { result } = renderHook(() => useUpdateRun(5), { wrapper }) await act(async () => { await result.current.mutateAsync({ name: 'Updated' } as never) }) expect(updateRun).toHaveBeenCalledWith(5, { name: 'Updated' }) }) it('invalidates both the list and individual run query on success', async () => { vi.mocked(updateRun).mockResolvedValue({} as never) const { queryClient, wrapper } = createWrapper() const spy = vi.spyOn(queryClient, 'invalidateQueries') const { result } = renderHook(() => useUpdateRun(5), { wrapper }) await act(async () => { await result.current.mutateAsync({} as never) }) expect(spy).toHaveBeenCalledWith({ queryKey: ['runs'] }) expect(spy).toHaveBeenCalledWith({ queryKey: ['runs', 5] }) }) it('shows a toast when status is set to completed', async () => { vi.mocked(updateRun).mockResolvedValue({} as never) const { wrapper } = createWrapper() const { result } = renderHook(() => useUpdateRun(1), { wrapper }) await act(async () => { await result.current.mutateAsync({ status: 'completed' } as never) }) expect(toast.success).toHaveBeenCalledWith('Run marked as completed!') }) it('shows an error toast on failure', async () => { vi.mocked(updateRun).mockRejectedValue(new Error('Network error')) const { wrapper } = createWrapper() const { result } = renderHook(() => useUpdateRun(1), { wrapper }) await act(async () => { await result.current.mutate({} as never) }) await waitFor(() => expect(toast.error).toHaveBeenCalledWith('Failed to update run: Network error') ) }) }) describe('useDeleteRun', () => { it('calls deleteRun with the given id', async () => { vi.mocked(deleteRun).mockResolvedValue(undefined as never) const { wrapper } = createWrapper() const { result } = renderHook(() => useDeleteRun(), { wrapper }) await act(async () => { await result.current.mutateAsync(7) }) expect(deleteRun).toHaveBeenCalledWith(7) }) it('invalidates the runs query on success', async () => { vi.mocked(deleteRun).mockResolvedValue(undefined as never) const { queryClient, wrapper } = createWrapper() const spy = vi.spyOn(queryClient, 'invalidateQueries') const { result } = renderHook(() => useDeleteRun(), { wrapper }) await act(async () => { await result.current.mutateAsync(7) }) expect(spy).toHaveBeenCalledWith({ queryKey: ['runs'] }) }) }) describe('useNamingCategories', () => { it('calls getNamingCategories', async () => { vi.mocked(getNamingCategories).mockResolvedValue([] as never) const { wrapper } = createWrapper() const { result } = renderHook(() => useNamingCategories(), { wrapper }) await waitFor(() => expect(result.current.isSuccess).toBe(true)) expect(getNamingCategories).toHaveBeenCalledOnce() }) }) describe('useNameSuggestions', () => { it('is disabled when runId is null', () => { const { wrapper } = createWrapper() const { result } = renderHook(() => useNameSuggestions(null), { wrapper }) expect(result.current.fetchStatus).toBe('idle') }) })