import { describe, expect, test } from "vitest"; import { buildAuthorAseInfoUrl, buildAuthorCommerceSeedBaseInfoUrl, createMarketApiClient, mapAuthorAseInfoResponse } from "../src/content/market/api-client"; describe("market-api-client", () => { test("builds the author ase info url with author id and range", () => { expect( buildAuthorAseInfoUrl("123", "https://xingtu.cn") ).toBe( "https://xingtu.cn/gw/api/aggregator/get_author_ase_info?author_id=123&range=30" ); }); test("maps a valid ASE payload into normalized rates", () => { expect( mapAuthorAseInfoResponse({ data: { avg_search_after_view_rate: "<0.02%", personal_avg_search_after_view_rate: "0.02 - 0.1%" } }) ).toMatchObject({ success: true, rates: { singleVideoAfterSearchRate: "<0.02%", personalVideoAfterSearchRate: "0.02% - 0.1%" } }); }); test("returns a missing-rate failure when the payload omits both rate fields", () => { expect( mapAuthorAseInfoResponse({ data: {} }) ).toMatchObject({ success: false, reason: "missing-rate" }); }); test("maps a partially populated payload into partial rates", () => { expect( mapAuthorAseInfoResponse({ data: { personal_avg_search_after_view_rate: "0.02 - 0.1%" } }) ).toMatchObject({ success: true, rates: { personalVideoAfterSearchRate: "0.02% - 0.1%" } }); }); test("returns a request-failed result for non-ok responses", async () => { const client = createMarketApiClient({ fetchImpl: async () => ({ ok: false, json: async () => ({}) }) }); await expect(client.loadAuthorAseInfo("123")).resolves.toMatchObject({ success: false, reason: "request-failed" }); }); test("loads rates from the commerce seed endpoint first", async () => { const requestedUrls: string[] = []; const client = createMarketApiClient({ fetchImpl: async (input) => { requestedUrls.push(input); return { ok: true, json: async () => ({ avg_search_after_view_rate: "0.1% - 0.25%", personal_avg_search_after_view_rate: "0.02% - 0.1%" }) }; } }); await expect(client.loadAuthorAseInfo("7363217488772857856")).resolves.toMatchObject( { success: true, rates: { singleVideoAfterSearchRate: "0.1% - 0.25%", personalVideoAfterSearchRate: "0.02% - 0.1%" } } ); expect(requestedUrls).toEqual([ "https://xingtu.cn/gw/api/aggregator/get_author_commerce_seed_base_info?o_author_id=7363217488772857856&range=90" ]); }); test("falls back to the ASE endpoint when the commerce seed endpoint fails", async () => { const requestedUrls: string[] = []; const client = createMarketApiClient({ fetchImpl: async (input) => { requestedUrls.push(input); if (input.includes("get_author_commerce_seed_base_info")) { return { ok: false, json: async () => ({}) }; } return { ok: true, json: async () => ({ data: { avg_search_after_view_rate: "<0.02%", personal_avg_search_after_view_rate: "0.02 - 0.1%" } }) }; } }); await expect(client.loadAuthorAseInfo("7363217488772857856")).resolves.toMatchObject( { success: true, rates: { singleVideoAfterSearchRate: "<0.02%", personalVideoAfterSearchRate: "0.02% - 0.1%" } } ); expect(requestedUrls).toEqual([ "https://xingtu.cn/gw/api/aggregator/get_author_commerce_seed_base_info?o_author_id=7363217488772857856&range=90", "https://xingtu.cn/gw/api/aggregator/get_author_ase_info?author_id=7363217488772857856&range=30" ]); }); test("returns a timeout result when the request aborts", async () => { const client = createMarketApiClient({ fetchImpl: async () => { throw new DOMException("Timed out", "AbortError"); }, timeoutMs: 1 }); await expect(client.loadAuthorAseInfo("123")).resolves.toMatchObject({ success: false, reason: "timeout" }); }); test("builds the author commerce seed info url with author id and range", () => { expect( buildAuthorCommerceSeedBaseInfoUrl("7363217488772857856", "https://xingtu.cn") ).toBe( "https://xingtu.cn/gw/api/aggregator/get_author_commerce_seed_base_info?o_author_id=7363217488772857856&range=90" ); }); });