diff --git a/packages/api-server/src/routes/ebay.ts b/packages/api-server/src/routes/ebay.ts index 06ebdca..9b95b8c 100644 --- a/packages/api-server/src/routes/ebay.ts +++ b/packages/api-server/src/routes/ebay.ts @@ -65,6 +65,7 @@ export async function ebayRoute(req: Request): Promise { keywords, buyItNowOnly, canadaOnly, + maxItems, }; const items = hideUnstableResults ? await fetchEbayItems(SEARCH_QUERY, 1, opts, { @@ -82,13 +83,7 @@ export async function ebayRoute(req: Request): Promise { { status: 404 }, ); - const results = hideUnstableResults - ? items - : maxItems !== undefined - ? items.slice(0, maxItems) - : items; - - return Response.json(results, { status: 200 }); + return Response.json(items, { status: 200 }); } catch (error) { console.error("eBay scraping error:", error); const errorMessage = diff --git a/packages/api-server/test/routes.test.ts b/packages/api-server/test/routes.test.ts index e194448..3402f35 100644 --- a/packages/api-server/test/routes.test.ts +++ b/packages/api-server/test/routes.test.ts @@ -382,21 +382,20 @@ describe("API routes", () => { expect(body.message).toBe("Search didn't return any results!"); }); - test("ebayRoute respects maxItems=0 in default mode", async () => { + test("ebayRoute forwards maxItems to core in default mode", async () => { const { ebayRoute } = await import("../src/routes/ebay"); fetchEbayItems.mockImplementation(() => - Promise.resolve([{ title: "a" }, { title: "b" }]), + Promise.resolve([{ title: "a" }]), ); - const response = await ebayRoute( + await ebayRoute( new Request( - "http://localhost/api/ebay?q=laptop&maxItems=0", + "http://localhost/api/ebay?q=laptop&maxItems=2", ), ); - const body = await response.json(); - expect(body).toHaveLength(0); + expect(fetchEbayItems).toHaveBeenCalledWith("laptop", 1, expect.objectContaining({ maxItems: 2 })); }); test("ebayRoute passes through scraper payload unchanged in unstable mode", async () => { @@ -420,6 +419,30 @@ describe("API routes", () => { expect(body.unstableResults).toHaveLength(2); expect(body.results[0].title).toBe("a"); expect(body.unstableResults[0].title).toBe("d"); + expect(fetchEbayItems).toHaveBeenCalledWith("laptop", 1, expect.objectContaining({ maxItems: 4 }), { + hideUnstableResults: true, + }); + }); + + test("ebayRoute forwards maxItems to core in unstable mode", async () => { + const { ebayRoute } = await import("../src/routes/ebay"); + + fetchEbayItems.mockImplementation(() => + Promise.resolve({ + results: [{ title: "a" }], + unstableResults: [{ title: "b" }], + }), + ); + + await ebayRoute( + new Request( + "http://localhost/api/ebay?q=laptop&unstableFilter=true&maxItems=2", + ), + ); + + expect(fetchEbayItems).toHaveBeenCalledWith("laptop", 1, expect.objectContaining({ maxItems: 2 }), { + hideUnstableResults: true, + }); }); test("ebayRoute returns 404 when unstable results are empty", async () => { diff --git a/packages/core/src/scrapers/ebay.ts b/packages/core/src/scrapers/ebay.ts index 5c12af6..c8d538b 100644 --- a/packages/core/src/scrapers/ebay.ts +++ b/packages/core/src/scrapers/ebay.ts @@ -394,6 +394,7 @@ export default async function fetchEbayItems( keywords?: string[]; buyItNowOnly?: boolean; canadaOnly?: boolean; + maxItems?: number; } | undefined, unstableMode: { hideUnstableResults: true }, ): Promise>; @@ -408,6 +409,7 @@ export default async function fetchEbayItems( keywords?: string[]; buyItNowOnly?: boolean; canadaOnly?: boolean; + maxItems?: number; }, unstableMode?: UnstableListingModeOptions, ): Promise; @@ -422,21 +424,12 @@ export default async function fetchEbayItems( keywords?: string[]; buyItNowOnly?: boolean; canadaOnly?: boolean; + maxItems?: number; } = {}, unstableMode: UnstableListingModeOptions = {}, ) { const requestsPerSecond = REQUESTS_PER_SECOND > 0 ? REQUESTS_PER_SECOND : 1; - const finalizeResults = ( - listings: EbayListingDetails[], - ): EbayListingDetails[] | UnstableListingBuckets => { - if (!unstableMode.hideUnstableResults) { - return listings; - } - - return classifyUnstableListings(listings); - }; - const { minPrice = 0, maxPrice = Number.MAX_SAFE_INTEGER, @@ -445,8 +438,22 @@ export default async function fetchEbayItems( keywords = [SEARCH_QUERY], // Default to search query if no keywords provided buyItNowOnly = true, canadaOnly = true, + maxItems, } = opts; + const finalizeResults = ( + listings: EbayListingDetails[], + ): EbayListingDetails[] | UnstableListingBuckets => { + const limitedListings = + maxItems !== undefined ? listings.slice(0, maxItems) : listings; + + if (!unstableMode.hideUnstableResults) { + return limitedListings; + } + + return classifyUnstableListings(limitedListings); + }; + const cookies = await loadEbayCookies(); // Build eBay search URL - use Canadian site, Buy It Now filter, and Canada-only preference diff --git a/packages/core/test/ebay-core.test.ts b/packages/core/test/ebay-core.test.ts index 95708e1..e03a603 100644 --- a/packages/core/test/ebay-core.test.ts +++ b/packages/core/test/ebay-core.test.ts @@ -552,4 +552,86 @@ describe("eBay Scraper Cookie Handling", () => { ], }); }); + + test("respects maxItems in default mode", async () => { + global.fetch = mock(() => + Promise.resolve({ + ok: true, + text: () => + Promise.resolve(` + +
  • + +

    First Bundle

    + CA $100.00 +
  • +
  • + +

    Second Bundle

    + CA $110.00 +
  • +
  • + +

    Third Bundle

    + CA $70.00 +
  • + + `), + }), + ) as typeof fetch; + + const results = await fetchEbayItems("laptop", 1000, { maxItems: 2 }); + + expect(results).toHaveLength(2); + expect(results[0]).toEqual( + expect.objectContaining({ title: "First Bundle" }), + ); + expect(results[1]).toEqual( + expect.objectContaining({ title: "Second Bundle" }), + ); + }); + + test("respects maxItems in unstable mode", async () => { + global.fetch = mock(() => + Promise.resolve({ + ok: true, + text: () => + Promise.resolve(` + +
  • + +

    First Bundle

    + CA $100.00 +
  • +
  • + +

    Second Bundle

    + CA $110.00 +
  • +
  • + +

    Third Bundle

    + CA $70.00 +
  • + + `), + }), + ) as typeof fetch; + + const results = await fetchEbayItems( + "laptop", + 1000, + { maxItems: 2 }, + { hideUnstableResults: true }, + ); + + expect(results.results).toHaveLength(2); + expect(results.unstableResults).toHaveLength(0); + expect(results.results[0]).toEqual( + expect.objectContaining({ title: "First Bundle" }), + ); + expect(results.results[1]).toEqual( + expect.objectContaining({ title: "Second Bundle" }), + ); + }); });