import { describe, expect, test, vi } from "vitest"; import { createFullScanController } from "../src/content/market/full-scan-controller"; import { createMarketResultStore } from "../src/content/market/result-store"; describe("full-scan-controller", () => { test("does not start a scan during initial construction", () => { const readCurrentPageRows = vi.fn(() => []); createFullScanController({ goToNextPage: async () => false, hasNextPage: () => false, loadAuthorMetrics: async () => ({ success: false as const, reason: "request-failed" as const }), readCurrentPageRows, resultStore: createMarketResultStore() }); expect(readCurrentPageRows).not.toHaveBeenCalled(); }); test("starts a full scan for filter actions", async () => { const harness = createHarness(); await harness.controller.ensureScanForFilter(); expect(harness.readCurrentPageRows).toHaveBeenCalledTimes(2); expect(harness.loadAuthorMetrics).toHaveBeenCalledTimes(2); }); test("starts a full scan for sort actions", async () => { const harness = createHarness(); await harness.controller.ensureScanForSort(); expect(harness.readCurrentPageRows).toHaveBeenCalledTimes(2); }); test("starts a full scan for export actions", async () => { const harness = createHarness(); await harness.controller.ensureScanForExport(); expect(harness.readCurrentPageRows).toHaveBeenCalledTimes(2); }); test("does not restart a completed scan unnecessarily", async () => { const harness = createHarness(); await harness.controller.ensureScanForFilter(); await harness.controller.ensureScanForSort(); expect(harness.readCurrentPageRows).toHaveBeenCalledTimes(2); expect(harness.loadAuthorMetrics).toHaveBeenCalledTimes(2); }); test("records failed author fetches without aborting the whole scan", async () => { const store = createMarketResultStore(); let pageIndex = 0; const pages = [ [ { authorId: "a", authorName: "Alpha" } ], [ { authorId: "b", authorName: "Beta" } ] ]; const controller = createFullScanController({ goToNextPage: async () => { pageIndex += 1; return true; }, hasNextPage: () => pageIndex < pages.length - 1, loadAuthorMetrics: async (authorId) => authorId === "a" ? { success: false as const, reason: "request-failed" as const } : { success: true as const, rates: { singleVideoAfterSearchRate: "0.5% - 1%", personalVideoAfterSearchRate: "0.02% - 0.1%" } }, readCurrentPageRows: vi.fn(() => pages[pageIndex]), resultStore: store }); await controller.ensureScanForExport(); expect(store.getRecord("a")).toMatchObject({ status: "failed", failureReason: "request-failed" }); expect(store.getRecord("b")).toMatchObject({ status: "success" }); }); }); function createHarness() { const store = createMarketResultStore(); let pageIndex = 0; const pages = [ [ { authorId: "a", authorName: "Alpha" } ], [ { authorId: "b", authorName: "Beta" } ] ]; const readCurrentPageRows = vi.fn(() => pages[pageIndex]); const loadAuthorMetrics = vi.fn(async () => ({ success: true as const, rates: { singleVideoAfterSearchRate: "0.5% - 1%", personalVideoAfterSearchRate: "0.02% - 0.1%" } })); return { controller: createFullScanController({ goToNextPage: async () => { pageIndex += 1; return true; }, hasNextPage: () => pageIndex < pages.length - 1, loadAuthorMetrics, readCurrentPageRows, resultStore: store }), loadAuthorMetrics, readCurrentPageRows, store }; }