syntaxai/tdd.md · main · src / b32_sitemap.test.ts

b32_sitemap.test.ts 116 lines · 3636 bytes raw
import { describe, expect, test } from "bun:test";
import {
  escapeXml,
  renderSitemap,
  STATIC_PATHS,
  type SitemapUrl,
} from "./b32_sitemap.ts";

describe("escapeXml", () => {
  test("escapes the five named entities", () => {
    expect(escapeXml("&")).toBe("&");
    expect(escapeXml("<")).toBe("&lt;");
    expect(escapeXml(">")).toBe("&gt;");
    expect(escapeXml('"')).toBe("&quot;");
    expect(escapeXml("'")).toBe("&apos;");
  });

  test("leaves regular URL characters untouched", () => {
    const s = "https://tdd.md/blog/2026-05/sama-v2-workingset-cross-repo-baseline";
    expect(escapeXml(s)).toBe(s);
  });

  test("ampersand always escapes first (no double-escape)", () => {
    expect(escapeXml("a & b < c")).toBe("a &amp; b &lt; c");
  });
});

describe("renderSitemap", () => {
  test("empty list → valid urlset with no <url> children", () => {
    const xml = renderSitemap([]);
    expect(xml).toBe(`<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
</urlset>`);
  });

  test("single URL with lastmod", () => {
    const xml = renderSitemap([
      { loc: "https://tdd.md/blog/x", lastmod: "2026-05-25" },
    ]);
    expect(xml).toBe(`<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url><loc>https://tdd.md/blog/x</loc><lastmod>2026-05-25</lastmod></url>
</urlset>`);
  });

  test("single URL without lastmod omits the <lastmod> element", () => {
    const xml = renderSitemap([{ loc: "https://tdd.md/sama" }]);
    expect(xml).toContain("<url><loc>https://tdd.md/sama</loc></url>");
    expect(xml).not.toContain("<lastmod>");
  });

  test("multiple URLs preserve input order", () => {
    const xml = renderSitemap([
      { loc: "https://tdd.md/a" },
      { loc: "https://tdd.md/b" },
      { loc: "https://tdd.md/c" },
    ]);
    const aIdx = xml.indexOf("/a</loc>");
    const bIdx = xml.indexOf("/b</loc>");
    const cIdx = xml.indexOf("/c</loc>");
    expect(aIdx).toBeGreaterThan(-1);
    expect(aIdx).toBeLessThan(bIdx);
    expect(bIdx).toBeLessThan(cIdx);
  });

  test("XML-escapes & and < inside <loc> values", () => {
    const xml = renderSitemap([
      { loc: "https://tdd.md/q?a=1&b=2" },
      { loc: "https://tdd.md/<weird>" },
    ]);
    expect(xml).toContain("<loc>https://tdd.md/q?a=1&amp;b=2</loc>");
    expect(xml).toContain("<loc>https://tdd.md/&lt;weird&gt;</loc>");
    expect(xml).not.toContain("a=1&b=2");
  });

  test("opens with the XML declaration and closes with </urlset>", () => {
    const xml = renderSitemap([{ loc: "https://tdd.md/" }]);
    expect(xml.startsWith('<?xml version="1.0" encoding="UTF-8"?>')).toBe(true);
    expect(xml.endsWith("</urlset>")).toBe(true);
  });

  test("deterministic — same input twice → byte-identical output", () => {
    const urls: ReadonlyArray<SitemapUrl> = [
      { loc: "https://tdd.md/", lastmod: "2026-05-25" },
      { loc: "https://tdd.md/blog" },
    ];
    expect(renderSitemap(urls)).toBe(renderSitemap(urls));
  });
});

describe("STATIC_PATHS", () => {
  test("covers the load-bearing routes (incl. /goals + /contributing)", () => {
    expect(STATIC_PATHS).toEqual([
      "/",
      "/blog",
      "/contributing",
      "/games",
      "/goals",
      "/leaderboard",
      "/sama",
      "/sama/v2",
      "/sama/v2/verify",
      "/sama/v2/example-crud",
      "/sama/v2/example-wordpress",
      "/sama/skill",
      "/guides",
    ]);
  });

  test("each path is absolute (starts with /)", () => {
    for (const p of STATIC_PATHS) {
      expect(p.startsWith("/")).toBe(true);
    }
  });
});