import { describe, expect, it } from "vitest"; import { fetchSitemapUrls, isSitemapIndex, parseSitemap } from "./sitemap.js"; const URLSET = ` http://x/a http://x/b http://x/q?x=1&y=2 `; const INDEX = ` http://x/sub1.xml http://x/sub2.xml `; describe("parseSitemap", () => { it("extracts URLs from entries and trims whitespace", () => { expect(parseSitemap(URLSET)).toEqual(["http://x/a", "http://x/b", "http://x/q?x=1&y=2"]); }); it("returns an empty list when there are no locs", () => { expect(parseSitemap("")).toEqual([]); }); }); describe("isSitemapIndex", () => { it("is true for a document", () => { expect(isSitemapIndex(INDEX)).toBe(true); }); it("is false for a plain ", () => { expect(isSitemapIndex(URLSET)).toBe(false); }); }); describe("fetchSitemapUrls", () => { it("returns the URLs from a plain sitemap", async () => { const urls = await fetchSitemapUrls("http://x/sitemap.xml", { fetcher: async () => URLSET, }); expect(urls).toEqual(["http://x/a", "http://x/b", "http://x/q?x=1&y=2"]); }); it("flattens a sitemap index, fetching each sub-sitemap once", async () => { const fetched: string[] = []; const urls = await fetchSitemapUrls("http://x/index.xml", { fetcher: async (src) => { fetched.push(src); if (src === "http://x/index.xml") return INDEX; if (src === "http://x/sub1.xml") { return `http://x/p1`; } return `http://x/p2`; }, }); expect(urls).toEqual(["http://x/p1", "http://x/p2"]); expect(fetched).toEqual(["http://x/index.xml", "http://x/sub1.xml", "http://x/sub2.xml"]); }); it("deduplicates URLs that appear in multiple sub-sitemaps", async () => { const urls = await fetchSitemapUrls("http://x/index.xml", { fetcher: async (src) => { if (src === "http://x/index.xml") return INDEX; return `http://x/same`; }, }); expect(urls).toEqual(["http://x/same"]); }); it("throws when expansion exceeds maxSitemaps", async () => { // A sitemap that references itself would cycle without this guard; we // simulate blow-up with a chain of 3 indexes and a limit of 2. const chain: Record = { "http://x/i0.xml": `http://x/i1.xml`, "http://x/i1.xml": `http://x/i2.xml`, "http://x/i2.xml": `http://x/end`, }; await expect( fetchSitemapUrls("http://x/i0.xml", { fetcher: async (src) => chain[src]!, maxSitemaps: 2, }) ).rejects.toThrow(/maxSitemaps=2/); }); });