feat(selection): show stock industry and concept boards in detail panel
Add point-in-time stock membership lookup to the sector radar module
(GET /sector-radar/stocks/{ts_code}/membership) reading the existing
dc_index/dc_member snapshots, and surface industries plus concept chips
in the selection signal detail panel with graceful no-data hiding.
This commit is contained in:
@@ -4,7 +4,11 @@ const requestJson = vi.hoisted(() => vi.fn())
|
||||
|
||||
vi.mock("@/shared/api/request-json", () => ({ requestJson }))
|
||||
|
||||
import { getSectorRadarDates, getSectorRadarRankings } from "./sector-radar.api"
|
||||
import {
|
||||
getSectorRadarDates,
|
||||
getSectorRadarRankings,
|
||||
getStockSectorMembership,
|
||||
} from "./sector-radar.api"
|
||||
|
||||
const publication = {
|
||||
publication_id: "publication-1",
|
||||
@@ -180,3 +184,59 @@ describe("sector radar API adapters", () => {
|
||||
).rejects.toThrow("rankings.rows[0].rank_percentile must be greater than 0")
|
||||
})
|
||||
})
|
||||
|
||||
describe("stock sector membership adapter", () => {
|
||||
const membershipPayload = {
|
||||
status: "success",
|
||||
ts_code: "000001.SZ",
|
||||
requested_trade_date: "2026-08-28",
|
||||
trade_date: "2026-08-28",
|
||||
industries: [{ code: "BK0901.DC", name: "银行" }],
|
||||
concepts: [
|
||||
{ code: "BK0001.DC", name: "机器人" },
|
||||
{ code: "BK0002.DC", name: "人工智能" },
|
||||
],
|
||||
concept_total: 2,
|
||||
concept_limit: 20,
|
||||
}
|
||||
|
||||
it("requests the snake-case endpoint and validates the payload", async () => {
|
||||
requestJson.mockResolvedValue(membershipPayload)
|
||||
|
||||
const result = await getStockSectorMembership("000001.SZ", "2026-08-28")
|
||||
|
||||
expect(requestJson).toHaveBeenCalledWith(
|
||||
"/api/v1/sector-radar/stocks/000001.SZ/membership?trade_date=2026-08-28",
|
||||
{ signal: undefined },
|
||||
)
|
||||
expect(result).toEqual(membershipPayload)
|
||||
})
|
||||
|
||||
it("keeps the no_data status with a null snapshot date", async () => {
|
||||
requestJson.mockResolvedValue({
|
||||
...membershipPayload,
|
||||
status: "no_data",
|
||||
trade_date: null,
|
||||
industries: [],
|
||||
concepts: [],
|
||||
concept_total: 0,
|
||||
})
|
||||
|
||||
const result = await getStockSectorMembership("000001.SZ", "2026-08-28")
|
||||
|
||||
expect(result.status).toBe("no_data")
|
||||
expect(result.trade_date).toBeNull()
|
||||
expect(result.industries).toEqual([])
|
||||
})
|
||||
|
||||
it("rejects a membership response with invalid sector references", async () => {
|
||||
requestJson.mockResolvedValue({
|
||||
...membershipPayload,
|
||||
industries: [{ code: "BK0901.DC", name: " " }],
|
||||
})
|
||||
|
||||
await expect(
|
||||
getStockSectorMembership("000001.SZ", "2026-08-28"),
|
||||
).rejects.toThrow("membership.industries[0].name must be a non-empty string")
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user