import { describe, expect, expectTypeOf, it } from "vitest"; import { getToolcraftRendererPipelinePassDefinition, isToolcraftRendererPipelineRegistration, registerToolcraftRendererPipeline, type ToolcraftRendererPipelinePassContract, } from "./renderer-pipeline-registration"; type PreviewModel = Readonly<{ opacity: number }>; type PreviewResource = Readonly<{ id: string }>; type TestPasses = { preview: ToolcraftRendererPipelinePassContract< PreviewModel, PreviewResource, readonly [resourceId: string] >; }; function createDescriptor() { return { interactionInvalidation: [ { interaction: "control-change" as const, invalidates: ["preview"], targets: ["appearance.opacity"], }, ], passes: [ { cacheKey: ["appearance.opacity"] as ["appearance.opacity"], id: "preview" as const, inputs: ["appearance.opacity"], invalidatedBy: ["appearance.opacity"], kind: "composite" as const, lifecycle: { cache: "retained-resource" as const, resourceScope: "renderer" as const, }, output: "preview" as const, quality: "full" as const, runsOn: "main" as const, }, ], runtimeId: "preview-runtime-v1", }; } describe("Toolcraft renderer pipeline registration", () => { it("compiles one branded immutable declaration and canonical pass handle", () => { const descriptor = createDescriptor(); const registration = registerToolcraftRendererPipeline()(descriptor); const handle = registration.getPass("preview"); expect(isToolcraftRendererPipelineRegistration(registration)).toBe(true); expect(registration.getPass("preview")).toBe(handle); expect(getToolcraftRendererPipelinePassDefinition(registration, handle)).toBe( registration.passes[0], ); expect(Object.isFrozen(registration)).toBe(true); expect(Object.isFrozen(registration.passes)).toBe(true); expect(Object.isFrozen(registration.passes[0])).toBe(true); expect(Object.isFrozen(registration.passes[0].lifecycle)).toBe(true); expect(Object.isFrozen(registration.interactionInvalidation[0])).toBe(true); (descriptor.passes[0] as { id: string }).id = "mutated"; descriptor.passes[0].cacheKey.push("appearance.opacity"); expect(registration.passes[0].id).toBe("preview"); expect(registration.passes[0].cacheKey).toEqual(["appearance.opacity"]); expectTypeOf(handle).toMatchTypeOf<{ readonly id: "preview"; }>(); }); it("returns only normalized supported fields", () => { const descriptor = { ...createDescriptor(), extension: "descriptor-extension", passes: [ { ...createDescriptor().passes[0], extension: "pass-extension", }, ], }; const registration = registerToolcraftRendererPipeline()(descriptor); expect(registration).not.toHaveProperty("extension"); expect(registration.passes[0]).not.toHaveProperty("extension"); if (false) { // @ts-expect-error Compiled registrations expose only normalized fields. void registration.extension; // @ts-expect-error Compiled passes expose only normalized fields. void registration.passes[0].extension; } }); it("requires exact contract coverage for compiled pass ids", () => { if (false) { const registerMissingContract = registerToolcraftRendererPipeline<{ other: ToolcraftRendererPipelinePassContract; }>(); // @ts-expect-error The preview descriptor requires a preview contract. registerMissingContract(createDescriptor()); const registerExtraContract = registerToolcraftRendererPipeline<{ preview: ToolcraftRendererPipelinePassContract; extra: ToolcraftRendererPipelinePassContract; }>(); // @ts-expect-error Contract maps cannot contain undeclared pass ids. registerExtraContract(createDescriptor()); } expect(true).toBe(true); }); it("rejects contradictory cache and resource capabilities", () => { const cacheNoneWithKey = { interactionInvalidation: [], passes: [ { cacheKey: ["value"], id: "preview", inputs: ["value"], invalidatedBy: ["value"], kind: "composite", lifecycle: { cache: "none", resourceScope: "call" }, output: "preview", quality: "full", runsOn: "main", }, ], runtimeId: "invalid-cache-v1", } as const; const registerNoResource = registerToolcraftRendererPipeline<{ preview: ToolcraftRendererPipelinePassContract; }>(); const retainedWithoutContract = { ...cacheNoneWithKey, passes: [ { ...cacheNoneWithKey.passes[0], lifecycle: { cache: "retained-resource", resourceScope: "renderer" }, }, ], runtimeId: "invalid-resource-v1", } as const; if (false) { // @ts-expect-error No-cache passes cannot declare executable cache keys. registerNoResource(cacheNoneWithKey); // @ts-expect-error Retained-resource passes require a resource contract. registerNoResource(retainedWithoutContract); } expect(() => registerNoResource(cacheNoneWithKey as never)).toThrow( 'Renderer pipeline pass "preview" with cache "none" cannot declare cacheKey.', ); const memoizedWithoutKey = { ...cacheNoneWithKey, passes: [ { ...cacheNoneWithKey.passes[0], cacheKey: undefined, lifecycle: { cache: "memoized", resourceScope: "renderer" }, }, ], } as const; const memoizedWithEmptyKey = { ...memoizedWithoutKey, passes: [{ ...memoizedWithoutKey.passes[0], cacheKey: [] }], } as const; if (false) { // @ts-expect-error Memoized passes require a non-empty cache key tuple. registerNoResource(memoizedWithoutKey); // @ts-expect-error Empty cache key tuples are not executable. registerNoResource(memoizedWithEmptyKey); } expect(() => registerNoResource(memoizedWithoutKey as never)).toThrow( 'Renderer pipeline pass "preview" with memoized cache requires cache key names.', ); expect(() => registerNoResource(memoizedWithEmptyKey as never)).toThrow( 'Renderer pipeline pass "preview" with memoized cache requires cache key names.', ); const registerResource = registerToolcraftRendererPipeline(); const retainedWithoutKey = { ...createDescriptor(), passes: [{ ...createDescriptor().passes[0], cacheKey: undefined }], } as const; const retainedWithEmptyKey = { ...createDescriptor(), passes: [{ ...createDescriptor().passes[0], cacheKey: [] }], } as const; if (false) { // @ts-expect-error Retained-resource passes require a non-empty cache key tuple. registerResource(retainedWithoutKey); // @ts-expect-error Empty retained-resource cache key tuples are not executable. registerResource(retainedWithEmptyKey); } expect(() => registerResource(retainedWithoutKey as never)).toThrow( 'Renderer pipeline pass "preview" with retained-resource cache requires cache key names.', ); expect(() => registerResource(retainedWithEmptyKey as never)).toThrow( 'Renderer pipeline pass "preview" with retained-resource cache requires cache key names.', ); }); it.each([ ["lifecycle.cache", { lifecycle: { cache: "persistent", resourceScope: "renderer" } }], ["lifecycle.resourceScope", { lifecycle: { cache: "memoized", resourceScope: "global" } }], ["kind", { kind: "blur" }], ["output", { output: "bitmap" }], ["quality", { quality: "draft" }], ["runsOn", { runsOn: "thread" }], ["cost.frequency", { cost: { dimensions: [], frequency: "continuous", relationship: "constant" } }], ["cost.relationship", { cost: { dimensions: [], frequency: "once", relationship: "fast" } }], ])("rejects unknown pass enum %s before branding", (path, override) => { const descriptor = createDescriptor(); descriptor.passes[0] = { ...descriptor.passes[0], ...override } as never; expect(() => registerToolcraftRendererPipeline()(descriptor as never), ).toThrow(`Renderer pipeline pass "preview" ${path}`); }); it("rejects unknown interactions before branding", () => { const descriptor = createDescriptor(); descriptor.interactionInvalidation[0].interaction = "hover" as never; expect(() => registerToolcraftRendererPipeline()(descriptor), ).toThrow('Renderer pipeline interaction "hover" is not supported.'); }); it.each([ [ "passes", () => ({ ...createDescriptor(), passes: "preview" }), "Renderer pipeline passes must be an array.", ], [ "interactionInvalidation", () => ({ ...createDescriptor(), interactionInvalidation: "control-change" }), "Renderer pipeline interactionInvalidation must be an array.", ], [ "pass inputs", () => { const descriptor = createDescriptor(); descriptor.passes[0].inputs = "appearance.opacity" as never; return descriptor; }, 'Renderer pipeline pass "preview" inputs must be an array of strings.', ], [ "pass invalidatedBy", () => { const descriptor = createDescriptor(); descriptor.passes[0].invalidatedBy = "appearance.opacity" as never; return descriptor; }, 'Renderer pipeline pass "preview" invalidatedBy must be an array of strings.', ], [ "pass cacheKey", () => { const descriptor = createDescriptor(); descriptor.passes[0].cacheKey = "appearance.opacity" as never; return descriptor; }, 'Renderer pipeline pass "preview" cacheKey must be an array of strings.', ], [ "cost dimensions", () => { const descriptor = createDescriptor(); descriptor.passes[0] = { ...descriptor.passes[0], cost: { dimensions: "preview-units", frequency: "once", relationship: "constant", }, } as never; return descriptor; }, 'Renderer pipeline pass "preview" cost.dimensions must be an array of strings.', ], [ "invalidation invalidates", () => { const descriptor = createDescriptor(); descriptor.interactionInvalidation[0].invalidates = "preview" as never; return descriptor; }, "Renderer pipeline interactionInvalidation[0].invalidates must be an array of strings.", ], [ "invalidation mustNotInvalidate", () => { const descriptor = createDescriptor(); ( descriptor.interactionInvalidation[0] as unknown as { mustNotInvalidate: unknown; } ).mustNotInvalidate = "preview"; return descriptor; }, "Renderer pipeline interactionInvalidation[0].mustNotInvalidate must be an array of strings.", ], [ "invalidation targets", () => { const descriptor = createDescriptor(); descriptor.interactionInvalidation[0].targets = "appearance.opacity" as never; return descriptor; }, "Renderer pipeline interactionInvalidation[0].targets must be an array of strings.", ], ])("rejects malformed JavaScript collection shape %s", (_label, create, message) => { expect(() => registerToolcraftRendererPipeline()(create() as never), ).toThrow(message); }); it("rejects duplicate ids and interaction references outside the compiled pass set", () => { const duplicate = createDescriptor(); duplicate.passes.push({ ...duplicate.passes[0] }); expect(() => registerToolcraftRendererPipeline()(duplicate)).toThrow( 'Renderer pipeline pass id "preview" must be unique.', ); const unknownInvalidation = createDescriptor(); unknownInvalidation.interactionInvalidation[0].invalidates = ["missing"]; expect(() => registerToolcraftRendererPipeline()(unknownInvalidation), ).toThrow( 'Renderer pipeline control-change invalidates undeclared pass "missing".', ); }); it("rejects unknown and foreign handles", () => { const first = registerToolcraftRendererPipeline()(createDescriptor()); const second = registerToolcraftRendererPipeline()(createDescriptor()); expect(() => first.getPass("missing" as "preview")).toThrow( 'Renderer pipeline registration "preview-runtime-v1" has no pass "missing".', ); expect(() => getToolcraftRendererPipelinePassDefinition(first, second.getPass("preview")), ).toThrow( 'Renderer pipeline pass "preview" does not belong to registration "preview-runtime-v1".', ); }); });