/*
Unit tests for feed covering buildRssFeed; buildJsonFeed. It also guards the documented public contract and regression-sensitive edge cases.
- Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only.
- RFC-1311: Compass header sweep — declares the feed suite's contract for test-file scaffold coverage.
*/
import { describe, expect, it } from "vitest";
import { buildJsonFeed, buildRssFeed } from "../feed.ts";
const channel = {
title: "Test Feed",
url: "https://example.com",
description: "A test feed",
language: "de",
selfUrl: "https://example.com/feed.xml",
};
const items = [
{
title: "Item 1",
url: "https://example.com/1",
summary: "Summary 1",
publishedAt: "2024-01-01",
},
{
title: "Item 2",
url: "https://example.com/2",
summary: "Summary 2",
publishedAt: "2024-06-01",
},
];
describe("buildRssFeed", () => {
it("produces valid RSS XML", () => {
const result = buildRssFeed(channel, items);
expect(result).toContain('");
expect(result).toContain("
Test Feed");
expect(result).toContain("de");
});
it("sorts items by publishedAt descending", () => {
const result = buildRssFeed(channel, items);
const item1Idx = result.indexOf("Item 1");
const item2Idx = result.indexOf("Item 2");
expect(item2Idx).toBeLessThan(item1Idx);
});
it("escapes XML special characters", () => {
const result = buildRssFeed(channel, [
{ title: "A & B ", url: "https://example.com/x", summary: "S", publishedAt: "2024-01-01" },
]);
expect(result).toContain("A & B <C>");
});
it("includes atom self link", () => {
const result = buildRssFeed(channel, items);
expect(result).toContain('href="https://example.com/feed.xml"');
expect(result).toContain('rel="self"');
});
it("derives lastBuildDate from latest item", () => {
const result = buildRssFeed(channel, items);
expect(result).toContain("");
});
it("omits lastBuildDate for empty feed", () => {
const result = buildRssFeed(channel, []);
expect(result).not.toContain("");
});
});
describe("buildJsonFeed", () => {
it("produces JSON Feed v1.1", () => {
const result = buildJsonFeed({
title: "Test Feed",
home_page_url: "https://example.com",
feed_url: "https://example.com/feed.json",
description: "A test feed",
language: "de",
items,
});
const parsed = JSON.parse(result);
expect(parsed.version).toBe("https://jsonfeed.org/version/1.1");
expect(parsed.title).toBe("Test Feed");
expect(parsed.items).toHaveLength(2);
});
it("sorts items by publishedAt descending", () => {
const result = buildJsonFeed({
title: "Test",
home_page_url: "https://example.com",
feed_url: "https://example.com/feed.json",
description: "",
language: "de",
items,
});
const parsed = JSON.parse(result);
expect(parsed.items[0].title).toBe("Item 2");
expect(parsed.items[1].title).toBe("Item 1");
});
it("includes date_modified when updatedAt provided", () => {
const result = buildJsonFeed({
title: "Test",
home_page_url: "https://example.com",
feed_url: "https://example.com/feed.json",
description: "",
language: "de",
items: [
{
title: "T",
url: "https://example.com/1",
summary: "S",
publishedAt: "2024-01-01",
updatedAt: "2024-06-01",
},
],
});
const parsed = JSON.parse(result);
expect(parsed.items[0].date_modified).toBe("2024-06-01");
});
it("omits items array for empty feed", () => {
const result = buildJsonFeed({
title: "Test",
home_page_url: "https://example.com",
feed_url: "https://example.com/feed.json",
description: "",
language: "de",
items: [],
});
const parsed = JSON.parse(result);
expect(parsed.items).toBeUndefined();
});
});