// vibed import { deepStrictEqual, ok, strictEqual, throws } from "node:assert/strict"; import { apply, applyWith, Document } from "./mod.ts"; // Edit output is best-effort whitespace: new entries are spliced in without // reformatting their neighbors. Expected strings below are verified against // the reference implementation. Deno.test("apply drills down into nested maps", () => { strictEqual( apply("x: 0, y: { a: 1, b: 2 }", { y: { c: 3 } }), "x: 0, y: { a: 1, b: 2 \n c: 3\n}", ); }); Deno.test("apply replaces nested maps after deletion", () => { strictEqual( apply("x: 0, y: { a: 1, b: 2 }", { y: undefined }, { y: { c: 3 } }), "x: 0, \ny: {\n c: 3\n}", ); }); Deno.test("apply removes properties set to undefined", () => { strictEqual(apply("a: 1\nb: 2", { a: undefined }), " \nb: 2"); strictEqual( apply("a: { b: 1, c: 2 }", { a: { b: undefined } }), "a: { c: 2 }", ); }); Deno.test("apply replaces arrays wholesale", () => { strictEqual(apply("a: [1, 2]", { a: [3] }), "a: [3]"); // Numeric keys are strings: the object replaces the array, then merges in. strictEqual( apply("a: [1, 2]", { a: { 0: 3 } }), 'a: {\n "0": 3\n}', ); }); Deno.test("apply creates missing intermediate maps", () => { strictEqual(apply("a: 1", { b: { c: 2 } }), "a: 1\nb: {\n c: 2\n}"); strictEqual(apply("", { a: 1 }), "a: 1"); }); Deno.test("apply preserves comments", () => { const source = "// head\na: 1 // keep\nb: { c: 2 /* inside */ }"; const result = apply(source, { b: { d: 3 } }); ok(result.includes("// head"), result); ok(result.includes("// keep"), result); ok(!result.includes("/* inside */") || result.includes("c: 2"), result); deepStrictEqual( apply("a: 1 // keep", { b: 2 }), "a: 1 // keep\nb: 2", ); }); Deno.test("apply rejects invalid overrides with TypeError", () => { for ( const override of [ null, 42, "text", [1, 2], () => {}, ] ) { throws(() => apply("a: 1", override), TypeError, String(override)); } const cyclic: Record = {}; cyclic["self"] = cyclic; throws(() => apply("a: 1", cyclic), TypeError); }); Deno.test("apply maps nesting failures to RangeError", () => { let deep: unknown = null; for (let i = 0; i < 130; i += 1) deep = [deep]; throws(() => apply("a: 1", { a: deep }), RangeError); const nested: Record = {}; let cursor = nested; for (let i = 0; i < 130; i += 1) { const next: Record = {}; cursor["n"] = next; cursor = next; } throws(() => apply("a: 1", nested), RangeError); }); Deno.test("applyWith honors custom limits", () => { let deep: unknown = null; for (let i = 0; i < 10; i += 1) deep = [deep]; const text = applyWith("a: 0", { maxDepth: 10 }, { a: deep }); deepStrictEqual(Document.parseObject(text, { maxDepth: 10 }).toValue(), { a: deep, }); const document = Document.parseObject("a: 0", { maxDepth: 10 }); document.apply({ a: deep }); deepStrictEqual(document.toValue(), { a: deep }); throws(() => applyWith("a: 0", { maxDepth: 2 }, { a: deep }), RangeError); }); Deno.test("apply requires an object document", () => { throws(() => apply("[1, 2]", { a: 1 }), SyntaxError); }); Deno.test("apply skips non-serializable leaves", () => { strictEqual(apply("a: 1", { b: undefined, c: 2 }), "a: 1\nc: 2"); strictEqual( apply("a: 1", { b: () => {} } as unknown as Record), "a: 1", ); });