import { test, expect, beforeEach, mock } from "bun:test"; let nextId = 0; function chainable(resolveWith: any[] = []): any { const p: any = Promise.resolve(resolveWith); p.limit = (_n: any) => chainable(resolveWith); p.where = (_cond: any) => chainable(resolveWith); p.from = (_table: any) => chainable(resolveWith); return p; } const makeMockDb = (): any => ({ insert: (_table: any) => ({ values: (_vals: any) => ({ returning: (_sel: any) => Promise.resolve([{ id: ++nextId }]), onConflictDoUpdate: (_conf: any) => Promise.resolve([]), }), }), update: (_table: any) => ({ set: (_vals: any) => ({ where: (_cond: any) => Promise.resolve([]), }), }), select: (_sel?: any) => ({ from: (_table: any) => ({ where: (_cond: any) => chainable(), limit: (_n: any) => chainable(), }), }), selectDistinct: (_sel: any) => ({ from: (_table: any) => chainable(), }), execute: (_query: any) => Promise.resolve([]), transaction: async (fn: (tx: any) => Promise) => fn(makeMockDb()), }); mock.module("./db/index.ts", () => ({ db: makeMockDb(), client: {} })); const fetchSellabilityBatchMock = mock(async (asins: string[]) => { return new Map( asins.map((asin) => [ asin, { canSell: true, sellabilityStatus: "available" as const, sellabilityReason: "ok", }, ]), ); }); const fetchSpApiPricingAndFeesMock = mock(async () => ({ fbaFee: 4, fbmFee: 2, referralFeePercent: 15, estimatedSalePrice: 25, canSell: true, sellabilityStatus: "available" as const, sellabilityReason: "ok", })); const analyzeProductsMock = mock(async (products: any[]) => { return products.map((p, idx) => ({ asin: p.record.asin, verdict: idx === 0 ? "FBA" : "FBM", confidence: 90, reasoning: "mocked", })); }); mock.module("./sp-api.ts", () => ({ fetchSellabilityBatch: fetchSellabilityBatchMock, fetchSpApiPricingAndFees: fetchSpApiPricingAndFeesMock, })); mock.module("./llm.ts", () => ({ analyzeProducts: analyzeProductsMock, })); const modulePromise = import("./bestsellers-by-category.ts"); let processCategory: (runId: number, category: any, perCategoryTop: number) => Promise; let insertCategoryRunSummary: (summary: any, runTimestamp: string) => Promise; let originalFetch: typeof globalThis.fetch; const mod = await modulePromise; processCategory = mod.processCategory; insertCategoryRunSummary = mod.insertCategoryRunSummary; originalFetch = globalThis.fetch; beforeEach(() => { nextId = 0; globalThis.fetch = mock(async (input: string | URL | Request) => { const rawUrl = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url; const url = new URL(rawUrl); if (url.pathname === "/bestsellers") { return new Response( JSON.stringify({ bestSellersList: ["B000000001", "B000000002"], tokensLeft: 10, refillRate: 1, }), { status: 200 }, ); } if (url.pathname === "/product") { return new Response( JSON.stringify({ products: [ { asin: "B000000001", title: "Product One", stats: { current: [null, null, null, 1000, null, null, null, null, null, null, null, 2, null, null, null, null, null, null, 2599], avg: [2400, null, null, 1200] }, csv: [[1, 2599]], categoryTree: [{ name: "Category 1" }], }, { asin: "B000000002", title: "Product Two", stats: { current: [null, null, null, 2000, null, null, null, null, null, null, null, 3, null, null, null, null, null, null, 1999], avg: [1800, null, null, 2200] }, csv: [[1, 1999]], categoryTree: [{ name: "Category 1" }], }, ], tokensLeft: 10, refillRate: 1, }), { status: 200 }, ); } return new Response("not found", { status: 404 }); }) as unknown as typeof globalThis.fetch; }); test("processCategory function test", async () => { const mockCategory = { id: 1, label: "Category 1", parentId: 0, childCount: 0, }; const runId = await insertCategoryRunSummary( { categoryId: mockCategory.id, categoryLabel: mockCategory.label, topAsinsChecked: 0, availableAsins: 0, fba: 0, fbm: 0, skip: 0, status: "running", error: "", results: [], }, new Date().toISOString(), ); const summary = await processCategory(runId, mockCategory, 2); expect(summary.status).toBe("ok"); expect(summary.topAsinsChecked).toBe(2); expect(summary.availableAsins).toBe(2); expect(summary.fba).toBe(1); expect(summary.fbm).toBe(1); expect(summary.results?.length).toBe(2); expect(summary.results?.[0]?.product.record.asin).toBe("B000000001"); expect(summary.results?.[0]?.verdict.verdict).toBe("FBA"); expect(summary.results?.[1]?.product.record.asin).toBe("B000000002"); expect(summary.results?.[1]?.verdict.verdict).toBe("FBM"); globalThis.fetch = originalFetch; });