/* Unit tests for substitute deep covering its documented public contract and regression-sensitive edge cases. Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only. RFC-1311: Compass header sweep — declares the substitute suite's contract for test-file scaffold coverage. */ import { test, expect } from "vitest"; import { substituteRefsDeep } from "../substitute-deep.ts"; // RFC-0138 fixture: a tiny disk-free stand-in for the RFC-0045 resolver. It models the // canonical business offer file so we can assert a {business.offer.price.*} reference written // in a block prop renders the resolved value — the validator/runtime alignment the RFC requires. const OFFER: Record = { "{business.offer.price.monthly}": "70 €/Monat", "{business.offer.price.yearly}": "700 €/Jahr", "{business.offer.price.setup}": "200 € Einrichtung", }; const REFERENCE_PATTERN = /\{[a-z]+\.[a-z0-9-]+\.[a-zA-Z0-9_.]+\}/g; async function fakeResolve(value: string): Promise { return value.replace(REFERENCE_PATTERN, (match) => OFFER[match] ?? ""); } test("substituteRefsDeep resolves a reference in a price-card prop", async () => { const props = { plan: "Standard", price: { monthly: "{business.offer.price.monthly}", yearly: "{business.offer.price.yearly}" }, }; const resolved = (await substituteRefsDeep(props, fakeResolve)) as typeof props; expect(resolved.price.monthly).toBe("70 €/Monat"); expect(resolved.price.yearly).toBe("700 €/Jahr"); // The literal brace string must not survive into the rendered prop. expect(!resolved.price.monthly.includes("{")).toBeTruthy(); }); test("substituteRefsDeep resolves references inside arrays", async () => { const props = { cards: [ { label: "Monat", value: "{business.offer.price.monthly}" }, { label: "Einrichtung", value: "{business.offer.price.setup}" }, ], }; const resolved = (await substituteRefsDeep(props, fakeResolve)) as typeof props; expect(resolved.cards[0].value).toBe("70 €/Monat"); expect(resolved.cards[1].value).toBe("200 € Einrichtung"); }); test("substituteRefsDeep passes non-string leaves through unchanged", async () => { const props = { enabled: true, order: 3, ratio: 0.5, nothing: null, nested: { animated: false, count: 0 }, }; const resolved = (await substituteRefsDeep(props, fakeResolve)) as typeof props; expect(resolved).toEqual(props); }); // ADR-0105: YAML timestamps arrive as Date instances in collection data. // Object.entries(new Date()) yields [], so rebuilding non-plain objects // silently replaced them with {} — rendering "Invalid Date" downstream. test("substituteRefsDeep passes Date instances through unchanged", async () => { const observedAt = new Date("2026-09-18T20:34:40.542Z"); const data = { assessment: { observedAt, seriesId: "{business.offer.price.monthly}", runs: [{ at: new Date("2026-09-18T21:16:58.750Z") }], }, }; const resolved = (await substituteRefsDeep(data, fakeResolve)) as typeof data; expect(resolved.assessment.observedAt).toBe(observedAt); expect(resolved.assessment.observedAt instanceof Date).toBeTruthy(); expect(resolved.assessment.runs[0].at instanceof Date).toBeTruthy(); // String leaves inside the same structure are still substituted. expect(resolved.assessment.seriesId).toBe("70 €/Monat"); }); test("substituteRefsDeep passes other class instances through unchanged", async () => { class Token { constructor(public value: string) {} } const token = new Token("{business.offer.price.setup}"); const data = { nested: { token, url: new URL("https://example.com/x") } }; const resolved = (await substituteRefsDeep(data, fakeResolve)) as typeof data; expect(resolved.nested.token).toBe(token); expect(resolved.nested.url).toBeInstanceOf(URL); }); test("substituteRefsDeep still rebuilds plain objects including null-prototype ones", async () => { const nullProto = Object.create(null) as Record; nullProto.field = "{business.offer.price.yearly}"; const data = { a: "{business.offer.price.monthly}", nullProto }; const resolved = (await substituteRefsDeep(data, fakeResolve)) as typeof data; expect(resolved.a).toBe("70 €/Monat"); expect(resolved.nullProto.field).toBe("700 €/Jahr"); }); test("substituteRefsDeep does not mutate the input", async () => { const props = { price: { monthly: "{business.offer.price.monthly}" } }; await substituteRefsDeep(props, fakeResolve); expect(props.price.monthly).toBe("{business.offer.price.monthly}"); });