chore: merge code-smell-cleanup
This commit is contained in:
@@ -33,6 +33,7 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () => Promise.resolve("<html><body></body></html>"),
|
||||
}),
|
||||
) as unknown as typeof fetch;
|
||||
@@ -70,6 +71,7 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () =>
|
||||
Promise.resolve(`
|
||||
<html><body>
|
||||
@@ -90,10 +92,26 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
test("returns empty results when eBay rate-limits the request", async () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: false,
|
||||
status: 429,
|
||||
headers: { get: () => "0" },
|
||||
text: () => Promise.resolve(""),
|
||||
}),
|
||||
) as unknown as typeof fetch;
|
||||
|
||||
const results = await fetchEbayItems("laptop", 1000);
|
||||
|
||||
expect(results).toEqual([]);
|
||||
});
|
||||
|
||||
test("deduplicates repeated item links from the same card", async () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () =>
|
||||
Promise.resolve(`
|
||||
<html><body>
|
||||
@@ -120,6 +138,7 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () =>
|
||||
Promise.resolve(`
|
||||
<html><body>
|
||||
@@ -152,6 +171,7 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () =>
|
||||
Promise.resolve(`
|
||||
<html><body>
|
||||
@@ -194,6 +214,7 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () =>
|
||||
Promise.resolve(`
|
||||
<html><body>
|
||||
@@ -295,6 +316,7 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () =>
|
||||
Promise.resolve(`
|
||||
<html><body>
|
||||
@@ -324,6 +346,7 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () =>
|
||||
Promise.resolve(`
|
||||
<html><body>
|
||||
@@ -353,6 +376,7 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () =>
|
||||
Promise.resolve(`
|
||||
<html><body>
|
||||
@@ -382,6 +406,7 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () =>
|
||||
Promise.resolve(`
|
||||
<html><body>
|
||||
@@ -424,6 +449,7 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () =>
|
||||
Promise.resolve(`
|
||||
<html><body>
|
||||
@@ -456,6 +482,7 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () =>
|
||||
Promise.resolve(`
|
||||
<html><body>
|
||||
@@ -488,6 +515,7 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () =>
|
||||
Promise.resolve(`
|
||||
<html><body>
|
||||
@@ -521,6 +549,7 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () =>
|
||||
Promise.resolve(`
|
||||
<html><body>
|
||||
@@ -548,6 +577,7 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () =>
|
||||
Promise.resolve(`
|
||||
<html><body>
|
||||
@@ -580,6 +610,7 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () =>
|
||||
Promise.resolve(`
|
||||
<html><body>
|
||||
@@ -610,6 +641,7 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () =>
|
||||
Promise.resolve(`
|
||||
<html><body>
|
||||
@@ -655,6 +687,7 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () =>
|
||||
Promise.resolve(`
|
||||
<html><body>
|
||||
@@ -693,6 +726,7 @@ describe("eBay Scraper Cookie Handling", () => {
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
headers: { get: () => null },
|
||||
text: () =>
|
||||
Promise.resolve(`
|
||||
<html><body>
|
||||
|
||||
@@ -38,4 +38,87 @@ describe("fetchHtml", () => {
|
||||
|
||||
expect(scheduledDelays).not.toContain(1000);
|
||||
});
|
||||
|
||||
test("fetchHtml returns responseUrl when includeResponseUrl is true", async () => {
|
||||
process.env.NODE_ENV = "test";
|
||||
global.fetch = mock(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
status: 200,
|
||||
url: "https://example.test/final",
|
||||
headers: { get: () => null },
|
||||
text: () => Promise.resolve("<html></html>"),
|
||||
}),
|
||||
) as unknown as typeof fetch;
|
||||
|
||||
const result = await fetchHtml("https://example.test", 0, {
|
||||
includeResponseUrl: true,
|
||||
});
|
||||
expect(result.html).toBe("<html></html>");
|
||||
expect(result.responseUrl).toBe("https://example.test/final");
|
||||
});
|
||||
|
||||
test("rate limit epoch reset uses bounded wait", async () => {
|
||||
process.env.NODE_ENV = "production";
|
||||
const scheduledDelays: number[] = [];
|
||||
const farFutureEpochSeconds = Math.floor(Date.now() / 1000) + 315_360_000;
|
||||
let calls = 0;
|
||||
|
||||
global.fetch = mock(() => {
|
||||
calls += 1;
|
||||
return Promise.resolve({
|
||||
ok: calls > 1,
|
||||
status: calls > 1 ? 200 : 429,
|
||||
url: "https://example.test",
|
||||
headers: {
|
||||
get: (name: string) =>
|
||||
name === "X-RateLimit-Reset" ? String(farFutureEpochSeconds) : null,
|
||||
},
|
||||
text: () => Promise.resolve("<html></html>"),
|
||||
});
|
||||
}) as unknown as typeof fetch;
|
||||
globalThis.setTimeout = mock((handler: TimerHandler, timeout?: number) => {
|
||||
scheduledDelays.push(Number(timeout));
|
||||
if (timeout !== 1_234_567 && typeof handler === "function") {
|
||||
handler();
|
||||
}
|
||||
return 0 as unknown as ReturnType<typeof setTimeout>;
|
||||
}) as unknown as typeof setTimeout;
|
||||
globalThis.clearTimeout = mock(() => {}) as unknown as typeof clearTimeout;
|
||||
|
||||
await fetchHtml("https://example.test", 0, {
|
||||
maxRetries: 1,
|
||||
timeoutMs: 1_234_567,
|
||||
});
|
||||
|
||||
expect(scheduledDelays).toContain(30_000);
|
||||
expect(scheduledDelays).not.toContain(farFutureEpochSeconds * 1000);
|
||||
});
|
||||
|
||||
test("custom Accept header overrides default accept without duplicate casing", async () => {
|
||||
process.env.NODE_ENV = "test";
|
||||
const customAccept = "text/plain";
|
||||
let requestHeaders: HeadersInit | undefined;
|
||||
|
||||
global.fetch = mock((_url: string | URL | Request, init?: RequestInit) => {
|
||||
requestHeaders = init?.headers;
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
status: 200,
|
||||
url: "https://example.test",
|
||||
headers: { get: () => null },
|
||||
text: () => Promise.resolve("<html></html>"),
|
||||
});
|
||||
}) as unknown as typeof fetch;
|
||||
|
||||
await fetchHtml("https://example.test", 0, {
|
||||
headers: { Accept: customAccept },
|
||||
});
|
||||
|
||||
expect(requestHeaders).toBeDefined();
|
||||
expect((requestHeaders as Record<string, string>).accept).toBe(
|
||||
customAccept,
|
||||
);
|
||||
expect((requestHeaders as Record<string, string>).Accept).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
// Test setup for Bun test runner
|
||||
// This file is loaded before any tests run due to bunfig.toml preload
|
||||
|
||||
// Mock fetch globally for tests
|
||||
global.fetch =
|
||||
global.fetch ||
|
||||
(() => {
|
||||
throw new Error("fetch is not available in test environment");
|
||||
});
|
||||
|
||||
// Add any global test utilities here
|
||||
global.fetch = Object.assign(
|
||||
() => {
|
||||
throw new Error("Tests must mock fetch explicitly");
|
||||
},
|
||||
{ preconnect: fetch.preconnect },
|
||||
) as typeof fetch;
|
||||
|
||||
Reference in New Issue
Block a user