import { describe, test, expect } from "bun:test"; import { resolveLocationId, resolveCategoryId, buildSearchUrl, HttpError, NetworkError, ParseError, RateLimitError, ValidationError } from "../src/kijiji"; describe("Location and Category Resolution", () => { describe("resolveLocationId", () => { test("should return numeric IDs as-is", () => { expect(resolveLocationId(1700272)).toBe(1700272); expect(resolveLocationId(0)).toBe(0); }); test("should resolve string location names", () => { expect(resolveLocationId("canada")).toBe(0); expect(resolveLocationId("ontario")).toBe(9004); expect(resolveLocationId("toronto")).toBe(1700273); expect(resolveLocationId("gta")).toBe(1700272); }); test("should handle case insensitive matching", () => { expect(resolveLocationId("Canada")).toBe(0); expect(resolveLocationId("ONTARIO")).toBe(9004); }); test("should default to Canada for unknown locations", () => { expect(resolveLocationId("unknown")).toBe(0); expect(resolveLocationId("")).toBe(0); }); test("should handle undefined input", () => { expect(resolveLocationId(undefined)).toBe(0); }); }); describe("resolveCategoryId", () => { test("should return numeric IDs as-is", () => { expect(resolveCategoryId(132)).toBe(132); expect(resolveCategoryId(0)).toBe(0); }); test("should resolve string category names", () => { expect(resolveCategoryId("all")).toBe(0); expect(resolveCategoryId("phones")).toBe(132); expect(resolveCategoryId("electronics")).toBe(29659001); expect(resolveCategoryId("buy-sell")).toBe(10); }); test("should handle case insensitive matching", () => { expect(resolveCategoryId("All")).toBe(0); expect(resolveCategoryId("PHONES")).toBe(132); }); test("should default to all categories for unknown categories", () => { expect(resolveCategoryId("unknown")).toBe(0); expect(resolveCategoryId("")).toBe(0); }); test("should handle undefined input", () => { expect(resolveCategoryId(undefined)).toBe(0); }); }); }); describe("URL Construction", () => { describe("buildSearchUrl", () => { test("should build basic search URL", () => { const url = buildSearchUrl("iphone", { location: 1700272, category: 132, sortBy: 'relevancy', sortOrder: 'desc', }); expect(url).toContain("b-buy-sell/canada/iphone/k0c132l1700272"); expect(url).toContain("sort=relevancyDesc"); expect(url).toContain("order=DESC"); }); test("should handle pagination", () => { const url = buildSearchUrl("iphone", { location: 1700272, category: 132, page: 2, }); expect(url).toContain("&page=2"); }); test("should handle different sort options", () => { const dateUrl = buildSearchUrl("iphone", { sortBy: 'date', sortOrder: 'asc', }); expect(dateUrl).toContain("sort=DATE"); expect(dateUrl).toContain("order=ASC"); const priceUrl = buildSearchUrl("iphone", { sortBy: 'price', sortOrder: 'desc', }); expect(priceUrl).toContain("sort=PRICE"); expect(priceUrl).toContain("order=DESC"); }); test("should handle string location/category inputs", () => { const url = buildSearchUrl("iphone", { location: "toronto", category: "phones", }); expect(url).toContain("k0c132l1700273"); // phones + toronto }); }); }); describe("Error Classes", () => { test("HttpError should store status and URL", () => { const error = new HttpError("Not found", 404, "https://example.com"); expect(error.message).toBe("Not found"); expect(error.status).toBe(404); expect(error.url).toBe("https://example.com"); expect(error.name).toBe("HttpError"); }); test("NetworkError should store URL and cause", () => { const cause = new Error("Connection failed"); const error = new NetworkError("Network error", "https://example.com", cause); expect(error.message).toBe("Network error"); expect(error.url).toBe("https://example.com"); expect(error.cause).toBe(cause); expect(error.name).toBe("NetworkError"); }); test("ParseError should store data", () => { const data = { invalid: "json" }; const error = new ParseError("Invalid JSON", data); expect(error.message).toBe("Invalid JSON"); expect(error.data).toBe(data); expect(error.name).toBe("ParseError"); }); test("RateLimitError should store URL and reset time", () => { const error = new RateLimitError("Rate limited", "https://example.com", 60); expect(error.message).toBe("Rate limited"); expect(error.url).toBe("https://example.com"); expect(error.resetTime).toBe(60); expect(error.name).toBe("RateLimitError"); }); test("ValidationError should work without field", () => { const error = new ValidationError("Invalid value"); expect(error.message).toBe("Invalid value"); expect(error.name).toBe("ValidationError"); }); });