import assert from "node:assert/strict"; import test from "node:test"; import { fetchAllH2BiDrill, fetchAllH2BiDrillRecords } from "./api"; import type { H2BiDrillResponse } from "./types"; const query = { year: 2026, vehicleScope: "all" as const, verifyScope: "all" as const, }; function response(overrides: Partial = {}): H2BiDrillResponse { return { groupBy: "station", amountScope: "all", filters: {}, summary: {}, groups: [], records: [], page: { page: 1, pageSize: 2, hasMore: false }, ...overrides, }; } async function withFetch( handler: (url: URL) => H2BiDrillResponse | Promise, run: () => Promise, ) { const original = globalThis.fetch; globalThis.fetch = (async (input: string | URL | Request) => { const url = new URL(String(input), "http://ln-bi.local"); return new Response(JSON.stringify(await handler(url)), { status: 200 }); }) as typeof fetch; try { await run(); } finally { globalThis.fetch = original; } } test("完整分组读取跨页,并以短页而非旧服务 hasMore 字段确认结束", async () => { const pages: number[] = []; await withFetch((url) => { const page = Number(url.searchParams.get("page")); pages.push(page); return response({ groups: page === 1 ? ["甲", "乙"].map((name, index) => ({ id: String(index), name, province: null, city: null, recordCount: 1, stationCount: 1, customerCount: 1, kg: 1, cost: 1, revenue: 1, lingniuKg: 1, externalKg: 0 })) : [{ id: "3", name: "丙", province: null, city: null, recordCount: 1, stationCount: 1, customerCount: 1, kg: 1, cost: 1, revenue: 1, lingniuKg: 1, externalKg: 0 }], }); }, async () => { const result = await fetchAllH2BiDrill({ ...query, groupBy: "station" }, { pageSize: 2 }); assert.deepEqual(result.groups.map((row) => row.name), ["甲", "乙", "丙"]); assert.equal(result.page.complete, true); assert.equal(result.page.pagesRead, 2); }); assert.deepEqual(pages, [1, 2]); }); test("完整记录读取跨页后保留全部订单", async () => { await withFetch((url) => { const page = Number(url.searchParams.get("page")); return response({ groupBy: "record", records: page === 1 ? [{ id: "a" }, { id: "b" }] : [{ id: "c" }], }); }, async () => { const result = await fetchAllH2BiDrillRecords(query, { pageSize: 2 }); assert.deepEqual(result.records.map((row) => row.id), ["a", "b", "c"]); assert.equal(result.page.hasMore, false); }); }); test("全量读取在中途请求失败时拒绝,不返回部分结果", async () => { const original = globalThis.fetch; globalThis.fetch = (async (input: string | URL | Request) => { const page = new URL(String(input), "http://ln-bi.local").searchParams.get("page"); if (page === "2") return new Response("failed", { status: 502, statusText: "Bad Gateway" }); return new Response(JSON.stringify(response({ groups: ["甲", "乙"].map((name, index) => ({ id: String(index), name, province: null, city: null, recordCount: 1, stationCount: 1, customerCount: 1, kg: 1, cost: 1, revenue: 1, lingniuKg: 1, externalKg: 0 })), })), { status: 200 }); }) as typeof fetch; try { await assert.rejects( fetchAllH2BiDrill({ ...query, groupBy: "station" }, { pageSize: 2 }), /API error: 502/, ); } finally { globalThis.fetch = original; } }); test("空结果是完整结果而不是加载失败", async () => { await withFetch(() => response(), async () => { const result = await fetchAllH2BiDrill({ ...query, groupBy: "station" }); assert.deepEqual(result.groups, []); assert.equal(result.page.pagesRead, 1); }); }); test("取消的全量读取不会发起请求或生成部分数据", async () => { const controller = new AbortController(); controller.abort(); await assert.rejects( fetchAllH2BiDrill({ ...query, groupBy: "station" }, { signal: controller.signal }), (error: Error) => error.name === "AbortError", ); }); test("全量读取在第一页完成后也会响应取消,不会请求下一页", async () => { const controller = new AbortController(); let calls = 0; await withFetch(() => { calls += 1; controller.abort(); return response({ groups: ["甲", "乙"].map((name, index) => ({ id: String(index), name, province: null, city: null, recordCount: 1, stationCount: 1, customerCount: 1, kg: 1, cost: 1, revenue: 1, lingniuKg: 1, externalKg: 0 })), }); }, async () => { await assert.rejects( fetchAllH2BiDrill({ ...query, groupBy: "station" }, { signal: controller.signal, pageSize: 2 }), (error: Error) => error.name === "AbortError", ); }); assert.equal(calls, 1); }); test("maxRows 和 maxPages 均拒绝不完整的全量读取", async () => { await withFetch(() => response({ groups: ["甲", "乙"].map((name, index) => ({ id: String(index), name, province: null, city: null, recordCount: 1, stationCount: 1, customerCount: 1, kg: 1, cost: 1, revenue: 1, lingniuKg: 1, externalKg: 0 })), }), async () => { await assert.rejects( fetchAllH2BiDrill({ ...query, groupBy: "station" }, { pageSize: 2, maxRows: 1 }), /超过 1 条保护上限/, ); await assert.rejects( fetchAllH2BiDrill({ ...query, groupBy: "station" }, { pageSize: 2, maxPages: 1 }), /超过 1 页保护上限/, ); }); });