// Unit tests for resolveSchema — basic resolution behavior. // Note: Robin's #3118 removed the filterAsCell parameter from resolveSchema, // so these tests only cover the single-argument form. import { describe, it } from "@std/testing/bdd"; import { expect } from "@std/expect"; import { resolveSchema, resolveSchemaForValue } from "../src/schema.ts"; import type { JSONSchema, JSONSchemaObj } from "../src/builder/types.ts"; import { isNontrivialSchema, toDeepFrozenSchema, } from "@commonfabric/data-model/schema-utils"; import { internSchema, isInternedSchema, } from "@commonfabric/data-model/schema-hash"; /** Narrow a JSONSchema | undefined to JSONSchemaObj or fail the test. */ function expectNontrivial( schema: JSONSchema | undefined, ): JSONSchemaObj { if (!isNontrivialSchema(schema)) { throw new Error("expected a nontrivial schema object"); } return schema; } describe("resolveSchema", () => { describe("basic behavior", () => { it("returns undefined for undefined", () => { expect(resolveSchema(undefined)).toBe(undefined); }); it("returns undefined for empty object", () => { expect(resolveSchema({})).toBe(undefined); }); it("returns undefined for boolean schema true", () => { expect(resolveSchema(true)).toBe(undefined); }); it("returns undefined for boolean schema false", () => { expect(resolveSchema(false)).toBe(undefined); }); it("returns an equal-but-frozen schema for a non-trivial schema", () => { const schema: JSONSchema = { type: "string" }; const got = resolveSchema(schema); expect(got).toEqual(schema); expect(Object.isFrozen(got)).toBe(true); }); it("preserves asCell in returned schema", () => { const schema: JSONSchema = { type: "string", asCell: ["cell"] }; const result = expectNontrivial(resolveSchema(schema)); expect(result.asCell).toEqual(["cell"]); }); it("preserves asCell stream in returned schema", () => { const schema: JSONSchema = { type: "string", asCell: ["stream"] }; const result = expectNontrivial(resolveSchema(schema)); expect(result.asCell).toEqual(["stream"]); }); }); describe("canonical intern contract", () => { // `resolveSchema` returns the canonical interned reference for its // input's structural content. These tests pin down what that means at // the edges. // // Markers use `${...}TestAt${Date.now()}-${Math.random()}` titles to // guarantee the input is not already in the intern cache from a prior // test run or module load — any static marker string could collide // with a previously-interned entry and silently invalidate a // "not yet interned" precondition. it("returns a deep-frozen interned schema for a non-trivial input", () => { // Pins the primitive contract: the returned schema passes // `isInternedSchema`. All the other tests in this describe block // verify downstream canonicalization behavior; this one confirms // the direct invariant that `resolveSchema` interns its output. const got = resolveSchema({ type: "string", title: `resolveSchemaInternedTestAt${Date.now()}-${Math.random()}`, }); expect(got).not.toBe(undefined); expect(isInternedSchema(got!)).toBe(true); }); it("returns an equal result for a deep-frozen-but-not-interned input", () => { const schema: JSONSchema = toDeepFrozenSchema({ type: "string", title: `resolveSchemaEqualTestAt${Date.now()}-${Math.random()}`, }); const got = resolveSchema(schema); expect(got).toEqual(schema); expect(Object.isFrozen(got)).toBe(true); }); it("returns the canonical interned reference when the input is content-equal to an already-interned schema", () => { const marker = `resolveSchemaCanonicalTestAt${Date.now()}-${Math.random()}`; // Intern a schema first, establishing the canonical reference. const canonical = internSchema({ type: "string", title: marker }); // Build a *different* (but content-equal) deep-frozen reference. const lookalike: JSONSchema = toDeepFrozenSchema({ type: "string", title: marker, }); // `resolveSchema` must return the canonical reference, not the // caller's lookalike. const got = resolveSchema(lookalike); expect(got).toBe(canonical); expect(got === lookalike).toBe(false); }); it("preserves the caller's reference when the input is already the canonical interned instance", () => { // An input that is itself the canonical instance short-circuits // through `internSchema`'s WeakMap and comes back unchanged. const canonical = internSchema({ type: "string", title: `resolveSchemaPreserveTestAt${Date.now()}-${Math.random()}`, }); expect(resolveSchema(canonical)).toBe(canonical); }); it("canonicalizes compound schemas across calls", () => { const marker = `resolveSchemaCompoundTestAt${Date.now()}-${Math.random()}`; // Two content-equal non-interned inputs produce the same reference // on their respective `resolveSchema` calls, because both are // canonicalized through the intern cache. const first = resolveSchema({ type: "object", title: marker, properties: { name: { type: "string" } }, }); const second = resolveSchema({ type: "object", title: marker, properties: { name: { type: "string" } }, }); expect(first).toBe(second); }); }); describe("resolveSchemaForValue", () => { it("does not select literal false compound branches", () => { const schema: JSONSchema = { anyOf: [ false, { type: "string", ifc: { integrity: ["string-branch"] }, }, ], }; const narrowed = expectNontrivial(resolveSchemaForValue(schema, "ok")); expect(narrowed.anyOf).toBe(undefined); expect(narrowed.type).toBe("string"); expect(narrowed.ifc).toEqual({ integrity: ["string-branch"] }); }); it("narrows union branches with nested refs to parent defs", () => { const schema: JSONSchema = { anyOf: [ { type: "object", properties: { payload: { $ref: "#/$defs/MessageAlice" }, }, required: ["payload"], }, { type: "object", properties: { payload: { $ref: "#/$defs/MessageBob" }, }, required: ["payload"], }, ], $defs: { MessageAlice: { type: "object", properties: { author: { type: "string", enum: ["alice"], }, }, required: ["author"], ifc: { integrity: [{ kind: "authored-by", subject: "alice" }], }, }, MessageBob: { type: "object", properties: { author: { type: "string", enum: ["bob"], }, }, required: ["author"], ifc: { integrity: [{ kind: "authored-by", subject: "bob" }], }, }, }, }; const narrowed = expectNontrivial( resolveSchemaForValue(schema, { payload: { author: "alice" } }), ); expect(narrowed.anyOf).toBe(undefined); expect(narrowed.properties?.payload).toMatchObject({ ifc: { integrity: [{ kind: "authored-by", subject: "alice" }], }, }); }); }); });