tests/unusedAttachments.test.ts
Rama main · réplica de lectura del repositorio original.
// Unused attachments (indexer `unreferencedAttachments()` + server/
// unusedAttachments.ts): every route a file is USED by keeps it off the
// list — an embed, a markdown image, a wikilink to a file, a banner, a
// tracker cover, a folder icon from settings, a drawing's own svg, a site
// image — and a file under `.trash/` is not in the vault at all.
//
// Over a fixture vault, the way tests/export.test.ts tests the archive: the
// answer comes from the index's own attachment walk, so the claim under
// test is "the list is the complement of what the app would show", and that
// is only checkable against real records.
import assert from "node:assert/strict";
import { after, before, describe, it } from "node:test";
import { initIndexer, unreferencedAttachments } from "../server/indexer.ts";
import { UNUSED_ATTACHMENTS_MAX, listUnusedAttachments } from "../server/unusedAttachments.ts";
import { patchSettings } from "../server/settings.ts";
import { initSite } from "../server/site.ts";
import { initVault } from "../server/vault.ts";
import { makeDir, makeVault, note, removeVault } from "./helpers/vault.ts";
const PNG = "\x89PNG\r\n\x1a\n0123";
const fence = (lines: string[]): string => ["```tracker", ...lines, "```"].join("\n");
const files: Record<string, string> = {
// Embedded by wikilink, by markdown image, and linked (not embedded).
"Essay.md": note({ banner: "hero.jpg" }, "# Essay\n\n![[figure.png]]\n\n\n\nSee [[paper.pdf]].\n"),
// A tracker's cover, which lives inside a code fence no markdown scanner reads.
"Shelf.md": note({}, `# Shelf\n\n${fence(["title: Dune", "kind: book", "progress: 1/2", "cover: dune.jpg"])}\n`),
// A folder note whose own icon is an image.
"Media/Media.md": note({ icon: "Media/mark.svg" }, "The media folder.\n"),
// A drawing keeps its exported picture beside it.
"Sketch.excalidraw": JSON.stringify({ type: "excalidraw", version: 2, elements: [], appState: {}, files: {} }),
"Sketch.excalidraw.svg": "<svg/>",
"Media/figure.png": PNG,
"Media/inline.png": PNG,
"Media/hero.jpg": PNG,
"Media/dune.jpg": PNG,
"Media/mark.svg": "<svg/>",
"Media/paper.pdf": "%PDF-1.4",
// Named by settings: a folder icon and the site logo.
"icons/folder.png": PNG,
"brand/logo.png": PNG,
// The ones nothing points at.
"Media/stale.png": PNG,
"attachments/old-recording.m4a": "\x00\x00",
"ملاحظات/صورة قديمة.png": PNG,
// Under `.trash/`: not in the vault, so neither used nor unused.
".trash/gone.png": PNG,
};
const data = makeDir();
const root = makeVault(files);
before(async () => {
initSite({ ASTROLABE_DATA: data });
initVault(root);
await initIndexer();
patchSettings({ folderIcons: { icons: "icons/folder.png" }, logo: "brand/logo.png" });
});
after(() => {
removeVault(root);
removeVault(data);
});
describe("unreferencedAttachments()", () => {
it("lists exactly the files no note, setting or drawing points at", () => {
assert.deepEqual(unreferencedAttachments(), [
"attachments/old-recording.m4a",
"Media/stale.png",
"ملاحظات/صورة قديمة.png",
]);
});
it("keeps every used route off the list", () => {
const unused = new Set(unreferencedAttachments());
for (const used of [
"Media/figure.png", // ![[embed]]
"Media/inline.png", // 
"Media/paper.pdf", // [[link]] to a file
"Media/hero.jpg", // banner:
"Media/dune.jpg", // tracker cover:
"Media/mark.svg", // folder note icon:
"Sketch.excalidraw.svg", // a drawing's export
"icons/folder.png", // settings.folderIcons
"brand/logo.png", // settings.logo
]) {
assert.equal(unused.has(used), false, `${used} should count as used`);
}
});
it("never sees .trash", () => {
assert.ok(!unreferencedAttachments().some((p) => p.startsWith(".trash/")));
});
});
describe("listUnusedAttachments()", () => {
it("stats each file and reports the true total", async () => {
const result = await listUnusedAttachments();
assert.equal(result.total, 3);
assert.equal(result.files.length, 3);
const stale = result.files.find((f) => f.path === "Media/stale.png");
assert.ok(stale);
// The fixture is written as UTF-8 (helpers/vault.ts), so the size is
// the encoded length, not the code-point count.
assert.equal(stale.size, Buffer.byteLength(PNG, "utf8"));
assert.ok(stale.mtimeMs > 0);
assert.ok(UNUSED_ATTACHMENTS_MAX >= result.files.length);
});
});