star-chart-search-enhancer/tests/full-scan-controller.test.ts

154 lines
3.9 KiB
TypeScript

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
};
}