Files
obsidian-foundry-sync/tests/push.test.ts
2026-06-20 19:15:38 +00:00

117 lines
4.7 KiB
TypeScript

import { describe, it, expect } from "vitest";
import { buildPushPayload } from "../src/push.js";
import { MapNameResolver, nameUuidIndexFromEntries } from "../src/resolver.js";
import type { JournalEntry } from "../src/types.js";
// A synthetic "live" Foundry entry as relay /get would return it. Crucially it has
// ownership/pages + a sibling flag (not campaign-codex) that must survive the push.
const liveEntry: JournalEntry = {
name: "Fenris (old)",
_id: "aaa",
folder: "fld1",
pages: ["page1", "page2"],
ownership: { default: 0, gm: 3 },
flags: {
"campaign-codex": { type: "npc", image: "uploads/mardonar/old.webp", data: { tags: ["npc"], associates: ["JournalEntry.bbb"] } },
"some-other-module": { keep: "me" },
},
};
const NOTE = `---
type: npc
race: "[[Dwarf]]"
faction: "[[House Quche]]"
tags:
- status/alive
- npc
portrait: "[[Fenris_portrait.png]]"
foundry:
cc_uuid: JournalEntry.aaa
cc_type: npc
folder_path: Campaign Codex - NPCs
contentHash: deadbeef
syncedAt: 2026-06-20T00:00:00.000Z
---
# Fenris of House Quche
*A grizzled dwarf.*
## Background
Fenris serves [[House Quche]] and knows [[Joron The Crab]].
## Secrets
He hides a map.
`;
const resolver = new MapNameResolver(nameUuidIndexFromEntries([
{ name: "House Quche", uuid: "JournalEntry.qqq" },
{ name: "Joron The Crab", uuid: "JournalEntry.bbb" },
{ name: "Dwarf", uuid: "JournalEntry.race-dwarf" },
]));
describe("buildPushPayload", () => {
it("overrides name + flags.campaign-codex, spreads the live entry", () => {
const { full } = buildPushPayload(NOTE, "Fenris of House Quche", liveEntry, resolver);
expect(full.name).toBe("Fenris of House Quche");
expect(full._id).toBe("aaa"); // spread from live entry
expect(full.pages).toEqual(["page1", "page2"]); // spread preserved
expect(full.ownership).toEqual({ default: 0, gm: 3 });
// The full doc overrides `flags` wholesale (only campaign-codex); the sibling
// flag is NOT on `full` — but it survives in Foundry because the /update diff
// uses a dot-path merge and never echoes it (asserted below).
expect(full.flags?.["some-other-module"]).toBeUndefined();
expect(full.flags?.["campaign-codex"]?.type).toBe("npc");
});
it("the /update diff carries only name + flags.campaign-codex (no _id/pages/ownership)", () => {
const { diff } = buildPushPayload(NOTE, "Fenris of House Quche", liveEntry, resolver);
expect(Object.keys(diff).sort()).toEqual(["flags.campaign-codex", "name"]);
expect(diff.name).toBe("Fenris of House Quche");
expect(diff._id).toBeUndefined();
expect(diff.pages).toBeUndefined();
expect(diff.ownership).toBeUndefined();
// The diff does NOT carry the sibling flag (dot-path merge on the live doc keeps it).
expect(diff["some-other-module"]).toBeUndefined();
});
it("resolves [[links]] -> @UUID via the map resolver (not the DB)", () => {
const { full } = buildPushPayload(NOTE, "Fenris of House Quche", liveEntry, resolver);
const desc = full.flags?.["campaign-codex"]?.data?.description ?? "";
expect(desc).toContain("@UUID[JournalEntry.qqq]{House Quche}");
expect(desc).toContain("@UUID[JournalEntry.bbb]{Joron The Crab}");
});
it("image override wins over the live entry's existing image", () => {
const { full } = buildPushPayload(NOTE, "Fenris of House Quche", liveEntry, resolver, "uploads/mardonar/new.png");
expect(full.flags?.["campaign-codex"]?.image).toBe("uploads/mardonar/new.png");
});
it("image undefined keeps the live entry's existing image", () => {
const { full } = buildPushPayload(NOTE, "Fenris of House Quche", liveEntry, resolver);
expect(full.flags?.["campaign-codex"]?.image).toBe("uploads/mardonar/old.webp");
});
it("image null clears the image", () => {
const { full } = buildPushPayload(NOTE, "Fenris of House Quche", liveEntry, resolver, null);
expect(full.flags?.["campaign-codex"]?.image).toBeNull();
});
it("preserves associates from the live entry's data (spread)", () => {
const { full } = buildPushPayload(NOTE, "Fenris of House Quche", liveEntry, resolver);
expect(full.flags?.["campaign-codex"]?.data?.associates).toEqual(["JournalEntry.bbb"]);
});
it("status/* tags are dropped from the cc tags (matching the offline path)", () => {
const { full } = buildPushPayload(NOTE, "Fenris of House Quche", liveEntry, resolver);
expect(full.flags?.["campaign-codex"]?.data?.tags).toEqual(["npc"]);
});
it("secrets section becomes notes HTML", () => {
const { full } = buildPushPayload(NOTE, "Fenris of House Quche", liveEntry, resolver);
const notes = full.flags?.["campaign-codex"]?.data?.notes ?? "";
expect(notes).toContain("map");
});
});