|
|
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(); |
|
|
}); |
|
|
}); |
|
|
|