Files
zhixing-system/zhixing-web/src/features/sector-radar/api/sector-radar.query.test.ts
T

200 lines
5.5 KiB
TypeScript
Raw Normal View History

import { beforeEach, describe, expect, it, vi } from "vitest"
const useQuery = vi.hoisted(() => vi.fn())
const useInfiniteQuery = vi.hoisted(() => vi.fn())
const api = vi.hoisted(() => ({
getSectorRadarHistory: vi.fn(),
getSectorRadarDetail: vi.fn(),
getSectorRadarDates: vi.fn(),
getSectorRadarRankings: vi.fn(),
}))
vi.mock("@tanstack/react-query", () => ({ useInfiniteQuery, useQuery }))
vi.mock("./sector-radar.api", () => api)
import {
sectorRadarSectorQueryKey,
useSectorRadarDetail,
useSectorRadarHistory,
sectorRadarDatesQueryKey,
sectorRadarRankingsQueryKey,
useSectorRadarDates,
useSectorRadarRankings,
} from "./sector-radar.query"
const query = {
tradeDate: "2026-08-28",
sectorType: "industry" as const,
view: "rank_change" as const,
rankChangeMetric: "ratio" as const,
rankChangeDays: 3,
side: "bottom" as const,
search: "银行",
page: 2,
pageSize: 50,
}
describe("sector radar query hooks", () => {
beforeEach(() => {
vi.clearAllMocks()
useQuery.mockImplementation((options) => options)
useInfiniteQuery.mockImplementation((options) => options)
})
it("isolates sector resources by type, code and date and forwards cancellation", () => {
const sector = {
sectorType: "concept" as const,
sectorCode: "BK01",
tradeDate: "2026-08-28",
}
const signal = new AbortController().signal
useSectorRadarDetail(sector)
const options = useQuery.mock.calls.at(-1)?.[0]
expect(options.queryKey).toEqual([
"sectorRadar",
"detail",
"concept",
"BK01",
"2026-08-28",
])
expect(options.placeholderData).toBeUndefined()
options.queryFn({ signal })
expect(api.getSectorRadarDetail).toHaveBeenCalledWith(sector, signal)
expect(
sectorRadarSectorQueryKey("detail", {
...sector,
tradeDate: "2026-08-31",
}),
).not.toEqual(options.queryKey)
expect(
sectorRadarSectorQueryKey("detail", {
...sector,
sectorType: "industry",
}),
).not.toEqual(options.queryKey)
useSectorRadarHistory(sector)
useQuery.mock.calls.at(-1)?.[0].queryFn({ signal })
expect(api.getSectorRadarHistory).toHaveBeenCalledWith(sector, signal)
})
it("keeps filters in one ranking key without splitting cached pages", () => {
expect(sectorRadarDatesQueryKey).toEqual(["sectorRadar", "dates"])
expect(sectorRadarRankingsQueryKey(query)).toEqual([
"sectorRadar",
"rankings",
"2026-08-28",
"industry",
"rank_change",
"ratio",
3,
"bottom",
"银行",
50,
])
expect(
sectorRadarRankingsQueryKey({ ...query, page: query.page + 1 }),
).toEqual(sectorRadarRankingsQueryKey(query))
})
it("forwards React Query cancellation signals to both adapters", async () => {
useSectorRadarDates()
useSectorRadarRankings(query)
const datesOptions = useQuery.mock.calls[0]?.[0] as {
queryFn: (context: { signal: AbortSignal }) => unknown
}
const rankingsOptions = useInfiniteQuery.mock.calls[0]?.[0] as {
queryFn: (context: { pageParam: number; signal: AbortSignal }) => unknown
}
const signal = new AbortController().signal
await datesOptions.queryFn({ signal })
await rankingsOptions.queryFn({ pageParam: 3, signal })
expect(api.getSectorRadarDates).toHaveBeenCalledWith(signal)
expect(api.getSectorRadarRankings).toHaveBeenCalledWith(
{ ...query, page: 3 },
signal,
)
})
it("starts from page one and stops after loaded rows cover total", () => {
useSectorRadarRankings(query)
const rankingsOptions = useInfiniteQuery.mock.calls[0]?.[0] as {
getNextPageParam: (
lastPage: { page: number; rows: unknown[]; total: number },
pages: { page: number; rows: unknown[]; total: number }[],
lastPageParam: number,
) => number | undefined
initialPageParam: number
}
const firstPage = {
page: 1,
rows: Array(50).fill(null),
status: "success",
total: 75,
}
const secondPage = {
page: 2,
rows: Array(25).fill(null),
status: "success",
total: 75,
}
expect(rankingsOptions.initialPageParam).toBe(1)
expect(rankingsOptions.getNextPageParam(firstPage, [firstPage], 1)).toBe(2)
expect(
rankingsOptions.getNextPageParam(secondPage, [firstPage, secondPage], 2),
).toBeUndefined()
})
it("stops when a page is empty or does not match its requested page param", () => {
useSectorRadarRankings(query)
const rankingsOptions = useInfiniteQuery.mock.calls[0]?.[0] as {
getNextPageParam: (
lastPage: {
page: number
rows: unknown[]
status: "success" | "no_data"
total: number
},
pages: {
page: number
rows: unknown[]
status: "success" | "no_data"
total: number
}[],
lastPageParam: number,
) => number | undefined
}
const firstPage = {
page: 1,
rows: Array(50).fill(null),
status: "success" as const,
total: 75,
}
const emptyPage = {
page: 2,
rows: [],
status: "success" as const,
total: 75,
}
const mismatchedPage = {
page: 1,
rows: Array(25).fill(null),
status: "success" as const,
total: 75,
}
expect(
rankingsOptions.getNextPageParam(emptyPage, [firstPage, emptyPage], 2),
).toBeUndefined()
expect(
rankingsOptions.getNextPageParam(
mismatchedPage,
[firstPage, mismatchedPage],
2,
),
).toBeUndefined()
})
})