diff --git a/packages/api-server/src/routes/ebay.ts b/packages/api-server/src/routes/ebay.ts index 21f0730..48652b7 100644 --- a/packages/api-server/src/routes/ebay.ts +++ b/packages/api-server/src/routes/ebay.ts @@ -21,8 +21,20 @@ export async function ebayRoute(req: Request): Promise { const minPriceParam = reqUrl.searchParams.get("minPrice"); const minPrice = minPriceParam ? parseInt(minPriceParam, 10) : undefined; + if (minPriceParam && Number.isNaN(minPrice)) { + return Response.json( + { message: "Invalid minPrice parameter" }, + { status: 400 }, + ); + } const maxPriceParam = reqUrl.searchParams.get("maxPrice"); const maxPrice = maxPriceParam ? parseInt(maxPriceParam, 10) : undefined; + if (maxPriceParam && Number.isNaN(maxPrice)) { + return Response.json( + { message: "Invalid maxPrice parameter" }, + { status: 400 }, + ); + } const strictMode = reqUrl.searchParams.get("strictMode") === "true"; const buyItNowOnly = reqUrl.searchParams.get("buyItNowOnly") !== "false"; const canadaOnly = reqUrl.searchParams.get("canadaOnly") !== "false"; @@ -71,28 +83,11 @@ export async function ebayRoute(req: Request): Promise { { status: 404 }, ); - let results; - if (hideUnstableResults) { - const limitedResults = - maxItems !== undefined - ? items.results.slice(0, maxItems) - : items.results; - const remainingSlots = - maxItems !== undefined - ? Math.max(0, maxItems - limitedResults.length) - : undefined; - const limitedUnstable = - remainingSlots !== undefined - ? items.unstableResults.slice(0, remainingSlots) - : items.unstableResults; - results = { - results: limitedResults, - unstableResults: limitedUnstable, - }; - } else { - results = - maxItems !== undefined ? items.slice(0, maxItems) : items; - } + const results = hideUnstableResults + ? items + : maxItems !== undefined + ? items.slice(0, maxItems) + : items; return Response.json(results, { status: 200 }); } catch (error) { diff --git a/packages/api-server/src/routes/kijiji.ts b/packages/api-server/src/routes/kijiji.ts index 153824c..f6aa972 100644 --- a/packages/api-server/src/routes/kijiji.ts +++ b/packages/api-server/src/routes/kijiji.ts @@ -19,10 +19,28 @@ export async function kijijiRoute(req: Request): Promise { const maxPagesParam = reqUrl.searchParams.get("maxPages"); const maxPages = maxPagesParam ? parseInt(maxPagesParam, 10) : 5; + if (maxPagesParam && Number.isNaN(maxPages)) { + return Response.json( + { message: "Invalid maxPages parameter" }, + { status: 400 }, + ); + } const priceMinParam = reqUrl.searchParams.get("priceMin"); const priceMin = priceMinParam ? parseInt(priceMinParam, 10) : undefined; + if (priceMinParam && Number.isNaN(priceMin)) { + return Response.json( + { message: "Invalid priceMin parameter" }, + { status: 400 }, + ); + } const priceMaxParam = reqUrl.searchParams.get("priceMax"); const priceMax = priceMaxParam ? parseInt(priceMaxParam, 10) : undefined; + if (priceMaxParam && Number.isNaN(priceMax)) { + return Response.json( + { message: "Invalid priceMax parameter" }, + { status: 400 }, + ); + } const hideUnstableResults = reqUrl.searchParams.get("unstableFilter") === "true"; diff --git a/packages/api-server/test/routes.test.ts b/packages/api-server/test/routes.test.ts index 2c221f6..3ec8d6c 100644 --- a/packages/api-server/test/routes.test.ts +++ b/packages/api-server/test/routes.test.ts @@ -399,7 +399,7 @@ describe("API routes", () => { expect(body).toHaveLength(0); }); - test("ebayRoute limits total items with maxItems in unstable mode", async () => { + test("ebayRoute passes through scraper payload unchanged in unstable mode", async () => { const { ebayRoute } = await import("../src/routes/ebay"); fetchEbayItems.mockImplementation(() => @@ -416,10 +416,8 @@ describe("API routes", () => { ); const body = await response.json(); - const total = body.results.length + body.unstableResults.length; - expect(total).toBe(4); expect(body.results).toHaveLength(3); - expect(body.unstableResults).toHaveLength(1); + expect(body.unstableResults).toHaveLength(2); expect(body.results[0].title).toBe("a"); expect(body.unstableResults[0].title).toBe("d"); }); @@ -472,4 +470,74 @@ describe("API routes", () => { const body = await response.json(); expect(body.message).toBe("Invalid maxItems parameter"); }); + + test("ebayRoute returns 400 for invalid minPrice", async () => { + const { ebayRoute } = await import("../src/routes/ebay"); + + const response = await ebayRoute( + new Request( + "http://localhost/api/ebay?q=laptop&minPrice=abc", + ), + ); + + expect(response.status).toBe(400); + const body = await response.json(); + expect(body.message).toBe("Invalid minPrice parameter"); + }); + + test("ebayRoute returns 400 for invalid maxPrice", async () => { + const { ebayRoute } = await import("../src/routes/ebay"); + + const response = await ebayRoute( + new Request( + "http://localhost/api/ebay?q=laptop&maxPrice=abc", + ), + ); + + expect(response.status).toBe(400); + const body = await response.json(); + expect(body.message).toBe("Invalid maxPrice parameter"); + }); + + test("kijijiRoute returns 400 for invalid maxPages", async () => { + const { kijijiRoute } = await import("../src/routes/kijiji"); + + const response = await kijijiRoute( + new Request( + "http://localhost/api/kijiji?q=laptop&maxPages=abc", + ), + ); + + expect(response.status).toBe(400); + const body = await response.json(); + expect(body.message).toBe("Invalid maxPages parameter"); + }); + + test("kijijiRoute returns 400 for invalid priceMin", async () => { + const { kijijiRoute } = await import("../src/routes/kijiji"); + + const response = await kijijiRoute( + new Request( + "http://localhost/api/kijiji?q=laptop&priceMin=abc", + ), + ); + + expect(response.status).toBe(400); + const body = await response.json(); + expect(body.message).toBe("Invalid priceMin parameter"); + }); + + test("kijijiRoute returns 400 for invalid priceMax", async () => { + const { kijijiRoute } = await import("../src/routes/kijiji"); + + const response = await kijijiRoute( + new Request( + "http://localhost/api/kijiji?q=laptop&priceMax=abc", + ), + ); + + expect(response.status).toBe(400); + const body = await response.json(); + expect(body.message).toBe("Invalid priceMax parameter"); + }); });