import { describe, expect, it } from "vitest"; import { publicOnly, toRows, usernameOf } from "../src/items.js"; import type { Item } from "../src/items.js"; /** a load_user_items row: diigo's actual field names (observed * 2026-10-07), made-up values */ function diigoItem(linkId: number, url: string): Item { return { link_id: linkId, url, title: `title ${linkId}`, description: "", private: false, u_name: "alice_99", first_by: "bob-the-first", user_id: 4242, real_name: "Alice A.", tags: "", created_at: 1700000000, updated_at: 1700000100 + linkId, annotations: [], comments: [], }; } function makeLog() { const entries: { level: string; args: unknown[] }[] = []; const at = (level: string) => (...args: unknown[]) => entries.push({ level, args }); return { log: { warn: at("warn") }, entries }; } describe("toRows", () => { it("keys each item on diigo's link_id, so a page of distinct items stays distinct", () => { const { log } = makeLog(); const rows = toRows([diigoItem(11, "https://a.example/"), diigoItem(12, "https://b.example/")], log); expect(rows.map((r) => r.itemId)).toEqual(["11", "12"]); }); it("logs, rather than silently drops or merges, items it cannot key", () => { const { log, entries } = makeLog(); const noId = diigoItem(13, "https://c.example/"); delete noId.link_id; const noUrl = diigoItem(14, ""); const rows = toRows([noId, noUrl, diigoItem(15, "https://d.example/")], log); expect(rows.map((r) => r.itemId)).toEqual(["15"]); expect(entries.map((e) => e.level)).toEqual(["warn", "warn"]); // diagnostics name the fields present, never their values expect(JSON.stringify(entries)).not.toContain("https://c.example/"); }); }); describe("usernameOf", () => { it("reads the owner's username from diigo's u_name", () => { expect(usernameOf(diigoItem(1, "https://a.example/"))).toBe("alice_99"); }); it("answers empty for an item without one", () => { expect(usernameOf(undefined)).toBe(""); expect(usernameOf({ u_name: "has spaces" })).toBe(""); }); }); describe("publicOnly", () => { it("keeps shared rows and drops private ones, before anything is stored", () => { const { log } = makeLog(); const shared = diigoItem(21, "https://shared.example/"); const secret = { ...diigoItem(22, "https://secret.example/"), private: true }; const rows = publicOnly(toRows([shared, secret], log)); expect(rows.map((r) => r.itemId)).toEqual(["21"]); expect(JSON.stringify(rows)).not.toContain("secret.example"); }); });