|
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' |
|
|
import { renderHook, waitFor, act } from '@testing-library/react' |
|
|
import { server } from '../../mocks/server' |
|
|
import { errorHandlers } from '../../mocks/handlers' |
|
|
import { useSegmentation } from '../useSegmentation' |
|
|
|
|
|
describe('useSegmentation', () => { |
|
|
beforeEach(() => { |
|
|
vi.useFakeTimers({ shouldAdvanceTime: true }) |
|
|
}) |
|
|
|
|
|
afterEach(() => { |
|
|
vi.useRealTimers() |
|
|
}) |
|
|
|
|
|
it('starts with null result and not loading', () => { |
|
|
const { result } = renderHook(() => useSegmentation()) |
|
|
|
|
|
expect(result.current.result).toBeNull() |
|
|
expect(result.current.isLoading).toBe(false) |
|
|
expect(result.current.error).toBeNull() |
|
|
expect(result.current.jobStatus).toBeNull() |
|
|
}) |
|
|
|
|
|
it('sets loading state and job status during segmentation', async () => { |
|
|
const { result } = renderHook(() => useSegmentation()) |
|
|
|
|
|
act(() => { |
|
|
result.current.runSegmentation('sub-stroke0001') |
|
|
}) |
|
|
|
|
|
expect(result.current.isLoading).toBe(true) |
|
|
|
|
|
|
|
|
await waitFor(() => { |
|
|
expect(result.current.jobId).toBeDefined() |
|
|
}) |
|
|
|
|
|
expect(result.current.jobStatus).toBeDefined() |
|
|
}) |
|
|
|
|
|
it('returns result on job completion', async () => { |
|
|
const { result } = renderHook(() => useSegmentation()) |
|
|
|
|
|
act(() => { |
|
|
result.current.runSegmentation('sub-stroke0001') |
|
|
}) |
|
|
|
|
|
|
|
|
await waitFor(() => { |
|
|
expect(result.current.jobId).toBeDefined() |
|
|
}) |
|
|
|
|
|
|
|
|
await act(async () => { |
|
|
await vi.advanceTimersByTimeAsync(5000) |
|
|
}) |
|
|
|
|
|
await waitFor(() => { |
|
|
expect(result.current.isLoading).toBe(false) |
|
|
expect(result.current.result).not.toBeNull() |
|
|
}) |
|
|
|
|
|
expect(result.current.result?.metrics.caseId).toBe('sub-stroke0001') |
|
|
expect(result.current.result?.metrics.diceScore).toBe(0.847) |
|
|
expect(result.current.result?.dwiUrl).toContain('dwi.nii.gz') |
|
|
}) |
|
|
|
|
|
it('shows progress updates during job execution', async () => { |
|
|
const { result } = renderHook(() => useSegmentation()) |
|
|
|
|
|
act(() => { |
|
|
result.current.runSegmentation('sub-stroke0001') |
|
|
}) |
|
|
|
|
|
|
|
|
await waitFor(() => { |
|
|
expect(result.current.jobId).toBeDefined() |
|
|
}) |
|
|
|
|
|
|
|
|
expect(result.current.progress).toBeGreaterThanOrEqual(0) |
|
|
expect(result.current.progressMessage).toBeDefined() |
|
|
}) |
|
|
|
|
|
it('sets error on job creation failure', async () => { |
|
|
server.use(errorHandlers.segmentCreateError) |
|
|
|
|
|
const { result } = renderHook(() => useSegmentation()) |
|
|
|
|
|
act(() => { |
|
|
result.current.runSegmentation('sub-stroke0001') |
|
|
}) |
|
|
|
|
|
await waitFor(() => { |
|
|
expect(result.current.isLoading).toBe(false) |
|
|
}) |
|
|
|
|
|
expect(result.current.error).toMatch(/failed to create job/i) |
|
|
expect(result.current.result).toBeNull() |
|
|
}) |
|
|
|
|
|
it('clears previous error on new request', async () => { |
|
|
server.use(errorHandlers.segmentCreateError) |
|
|
const { result } = renderHook(() => useSegmentation()) |
|
|
|
|
|
|
|
|
act(() => { |
|
|
result.current.runSegmentation('sub-stroke0001') |
|
|
}) |
|
|
|
|
|
await waitFor(() => { |
|
|
expect(result.current.error).not.toBeNull() |
|
|
}) |
|
|
|
|
|
|
|
|
server.resetHandlers() |
|
|
|
|
|
|
|
|
act(() => { |
|
|
result.current.runSegmentation('sub-stroke0001') |
|
|
}) |
|
|
|
|
|
expect(result.current.error).toBeNull() |
|
|
expect(result.current.isLoading).toBe(true) |
|
|
}) |
|
|
|
|
|
it('can cancel a running job', async () => { |
|
|
const { result } = renderHook(() => useSegmentation()) |
|
|
|
|
|
act(() => { |
|
|
result.current.runSegmentation('sub-stroke0001') |
|
|
}) |
|
|
|
|
|
await waitFor(() => { |
|
|
expect(result.current.isLoading).toBe(true) |
|
|
}) |
|
|
|
|
|
|
|
|
act(() => { |
|
|
result.current.cancelJob() |
|
|
}) |
|
|
|
|
|
expect(result.current.isLoading).toBe(false) |
|
|
expect(result.current.jobStatus).toBeNull() |
|
|
}) |
|
|
|
|
|
it('cleans up polling on unmount', async () => { |
|
|
const { result, unmount } = renderHook(() => useSegmentation()) |
|
|
|
|
|
act(() => { |
|
|
result.current.runSegmentation('sub-stroke0001') |
|
|
}) |
|
|
|
|
|
await waitFor(() => { |
|
|
expect(result.current.isLoading).toBe(true) |
|
|
}) |
|
|
|
|
|
|
|
|
unmount() |
|
|
}) |
|
|
}) |
|
|
|