diigo-cache: RSS in diigo's export shape, from cached items
src/rss.ts renders items as RSS 2.0 in the shape of diigo's RSS export: title, link, and a description packing Tags:…;Description:…; Annotations and Sticky notes:… (Highlight:/Sticky notes: lines). Unlike the export it is well-formed (characters XML forbids are dropped; the export carries some raw) and keeps whole titles (the export cuts at 255). Pure: dates come from the caller. scripts/compare-export.ts (a program script: node, node:sqlite) loads a cache's items, writes the RSS, and compares it with an export, keyed on link, reporting counts by category. Generated-by: Claude Opus 5.5
This commit is contained in:
1 parent
6af05190a6
commit
e6deb9fda0
3 files changed
+204
No files matched your search
@@ -0,0 +1,50 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { itemDescription, rssItem, toRss } from "../src/rss.js";
|
||||
|
||||
// shaped like diigo's export (field layout observed 2026-10-07), made-up values
|
||||
|
||||
describe("itemDescription", () => {
|
||||
it("packs tags, description, and annotations the way diigo's export does", () => {
|
||||
expect(
|
||||
itemDescription({
|
||||
tags: "ocap,js",
|
||||
description: "a note",
|
||||
annotations: [
|
||||
{ content: "first highlight", stickyNotes: [] },
|
||||
{ content: "second", stickyNotes: [{ content: "a sticky\n\nwith lines" }, { content: "another" }] },
|
||||
],
|
||||
}),
|
||||
).toBe(
|
||||
"Tags:ocap,js;Description:a note;Annotations and Sticky notes:" +
|
||||
"Highlight:first highlight\n" +
|
||||
"Highlight:second\nSticky notes:a sticky\n\nwith lines\nSticky notes:another\n",
|
||||
);
|
||||
});
|
||||
|
||||
it("says no_tag when there are no tags, and leaves empty parts empty", () => {
|
||||
expect(itemDescription({ tags: "", description: "", annotations: [] })).toBe(
|
||||
"Tags:no_tag;Description:;Annotations and Sticky notes:",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("rssItem / toRss", () => {
|
||||
it("is well-formed even when diigo's text isn't: forbidden characters dropped, ]]> split", () => {
|
||||
const xml = rssItem({ title: "bad\u0003char", url: "https://a.example/", tags: "", description: "x]]>y" });
|
||||
expect(xml).not.toContain("\u0003");
|
||||
expect(xml).toContain("<title><![CDATA[badchar]]></title>");
|
||||
expect(xml).toContain("Description:x]]]]><![CDATA[>y;");
|
||||
});
|
||||
|
||||
it("keeps the whole title (diigo's export cuts at 255)", () => {
|
||||
const title = "t".repeat(300);
|
||||
expect(rssItem({ title, url: "u" })).toContain(title);
|
||||
});
|
||||
|
||||
it("wraps items in an RSS 2.0 channel, dated by the caller", () => {
|
||||
const feed = toRss([{ title: "a", url: "u" }], { title: "my items", pubDate: "Wed, 07 Oct 2026 12:00:00 +0000" });
|
||||
expect(feed).toMatch(/^<\?xml version="1.0"\?>\n<rss version="2.0">\n<channel>/);
|
||||
expect(feed).toContain("<pubDate>Wed, 07 Oct 2026 12:00:00 +0000</pubDate>");
|
||||
expect(feed).toContain("<item><title><![CDATA[a]]></title>");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user