import { describe, expect, it } from "vitest"; import { normalizeControlsPanelLayout } from "./controls-panel-normalization"; import { defineToolcraft } from "./define-toolcraft"; import type { ToolcraftAppSchema, ToolcraftControlSectionSchema, } from "./types"; const opacityControl = { defaultValue: 75, max: 100, min: 0, target: "appearance.opacity", type: "slider", } as const; function cloneSectionWithoutInternalMarker( section: ToolcraftControlSectionSchema | undefined, ): ToolcraftControlSectionSchema { return JSON.parse(JSON.stringify(section)) as ToolcraftControlSectionSchema; } function createSchemaWithGeneratedExport() { return defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [ { controls: { export: { actions: [{ label: "Export PNG", value: "export" }], target: "output.export", type: "panelActions", }, }, title: "Export", }, ], title: "Controls", }, }, }); } describe("Toolcraft control section identity", () => { it.each(["appearance", "product.section", "product-section_2"])( "preserves a valid explicit stable section id %j byte-for-byte", (id) => { const panel = normalizeControlsPanelLayout({ sections: [ { controls: { opacity: opacityControl }, id, title: "Appearance", }, ], title: "Controls", }); expect(panel.sections[0]?.id).toBe(id); }, ); it.each([ "", " appearance ", "Appearance", "appearance controls", "appearance!", ".appearance", "appearance..controls", ])("rejects an invalid explicit id %j", (id) => { expect(() => normalizeControlsPanelLayout({ sections: [{ controls: { opacity: opacityControl }, id }], title: "Controls", }), ).toThrow(/lowercase ASCII segments/iu); }); it("derives a deterministic legacy id from authored controls, not title or visibility", () => { const first = normalizeControlsPanelLayout({ sections: [ { controls: { opacity: opacityControl }, title: "Appearance", visibleWhen: { equals: true, target: "mode.enabled" }, }, ], title: "Controls", }).sections[0]?.id; const second = normalizeControlsPanelLayout({ sections: [ { controls: { renamedOpacity: opacityControl }, title: "Renamed section", visibleWhen: { equals: false, target: "different.runtime.value" }, }, ], title: "Controls", }).sections[0]?.id; expect(first).toBe(second); expect(first).toMatch(/^legacy\./u); expect(first).not.toContain(":0:"); }); it("derives a valid deterministic legacy id from targets with repeated separators", () => { const createResolvedSchema = () => defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [ { controls: { opacity: { ...opacityControl, target: "appearance..opacity", }, }, title: "Appearance", }, ], title: "Controls", }, }, }); const resolved = createResolvedSchema(); const legacyId = resolved.panels.controls?.sections.find((section) => section.id.startsWith("legacy."), )?.id; expect(legacyId).toMatch(/^legacy\.[a-z0-9]+(?:[._-][a-z0-9]+)*$/u); expect( createResolvedSchema().panels.controls?.sections.find((section) => section.id.startsWith("legacy."), )?.id, ).toBe(legacyId); expect( defineToolcraft(resolved as unknown as ToolcraftAppSchema).panels.controls?.sections.find( (section) => section.id.startsWith("legacy."), )?.id, ).toBe(legacyId); }); it("hashes distinct target seed arrays to distinct legacy ids", () => { const getLegacyId = (targets: readonly string[]) => { const controls = Object.fromEntries( targets.map((target, index) => [ `control${index}`, { ...opacityControl, target }, ]), ); const resolved = defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [{ controls, title: "Seed collision" }], title: "Controls", }, }, }); return resolved.panels.controls?.sections.find((section) => section.id.startsWith("legacy."), )?.id; }; expect(getLegacyId(["a", "b|c", "d"])).not.toBe( getLegacyId(["a", "b", "c|d"]), ); }); it("rejects duplicate resolved section ids", () => { expect(() => normalizeControlsPanelLayout({ sections: [ { controls: { opacity: opacityControl }, id: "appearance", }, { controls: { strength: { ...opacityControl, target: "appearance.strength", }, }, id: "appearance", }, ], title: "Controls", }), ).toThrow(/duplicate control section id "appearance"/iu); }); it.each([ "runtime.setup", "runtime.export", "legacy.appearance.deadbeef", "appearance.part-appearance.opacity-deadbeef", ])( "rejects a public authored reserved id %j", (id) => { expect(() => normalizeControlsPanelLayout({ sections: [ { controls: { opacity: opacityControl }, id, title: "Appearance", }, ], title: "Controls", }), ).toThrow(/reserved .* namespace/iu); }, ); it.each(["runtime.setup", "runtime.export"])( "rejects an exact unmarked public lookalike for generated section %j", (reservedId) => { const generated = createSchemaWithGeneratedExport(); const generatedSection = generated.panels.controls?.sections.find( (section) => section.id === reservedId, ); const forgedSection = cloneSectionWithoutInternalMarker(generatedSection); expect(() => defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [forgedSection], title: "Controls", }, }, }), ).toThrow(/reserved runtime namespace/iu); }, ); it("does not expose internal provenance as own section symbols", () => { const generated = createSchemaWithGeneratedExport(); const reservedSections = generated.panels.controls?.sections.filter((section) => section.id.startsWith("runtime."), ); expect(reservedSections).toHaveLength(2); expect( reservedSections?.every( (section) => Object.getOwnPropertySymbols(section).length === 0, ), ).toBe(true); }); it("rejects provenance forged from every discoverable setup descriptor", () => { const generated = createSchemaWithGeneratedExport(); const setupSection = generated.panels.controls?.sections.find( (section) => section.id === "runtime.setup", ); const exportSection = generated.panels.controls?.sections.find( (section) => section.id === "runtime.export", ); const forgedSection = Object.create( Object.getPrototypeOf(setupSection), Object.getOwnPropertyDescriptors(setupSection), ) as ToolcraftControlSectionSchema; Object.assign(forgedSection, cloneSectionWithoutInternalMarker(exportSection)); for (const symbol of Object.getOwnPropertySymbols(setupSection ?? {})) { const descriptor = Object.getOwnPropertyDescriptor(setupSection ?? {}, symbol); Object.defineProperty(forgedSection, symbol, { ...descriptor, value: "runtime.export", }); } expect(() => defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [forgedSection], title: "Controls", }, }, }), ).toThrow(/reserved runtime namespace/iu); }); it.each(["spread", "structuredClone"] as const)( "rejects a %s copy of a reserved internal section", (cloneKind) => { const generated = createSchemaWithGeneratedExport(); const exportSection = generated.panels.controls?.sections.find( (section) => section.id === "runtime.export", ); const clonedSection = cloneKind === "spread" ? { ...exportSection } : structuredClone(exportSection); expect(() => defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [clonedSection as ToolcraftControlSectionSchema], title: "Controls", }, }, }), ).toThrow(/reserved runtime namespace/iu); }, ); it("rejects an exact unmarked public lookalike for a legacy-derived section", () => { const generated = defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [ { controls: { opacity: opacityControl }, title: "Appearance", }, ], title: "Controls", }, }, }); const generatedSection = generated.panels.controls?.sections.find((section) => section.id.startsWith("legacy."), ); expect(() => defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [cloneSectionWithoutInternalMarker(generatedSection)], title: "Controls", }, }, }), ).toThrow(/reserved legacy namespace/iu); }); it("rejects an exact unmarked public lookalike for a mixed-layout child", () => { const generated = defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [ { controls: { opacity: opacityControl, palette: { target: "appearance.palette", type: "palette", }, }, id: "appearance", title: "Appearance", }, ], title: "Controls", }, }, }); const generatedSection = generated.panels.controls?.sections.find((section) => section.id.startsWith("appearance.part-"), ); expect(() => defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [cloneSectionWithoutInternalMarker(generatedSection)], title: "Controls", }, }, }), ).toThrow(/reserved internally derived namespace/iu); }); it("uses reserved stable ids for runtime setup and export", () => { const schema = defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [ { controls: { export: { actions: [{ label: "Export PNG", value: "export" }], target: "output.export", type: "panelActions", }, }, id: "output-actions", }, ], title: "Controls", }, }, }); expect(schema.panels.controls?.sections[0]?.id).toBe("runtime.setup"); expect(schema.panels.controls?.sections.at(-1)?.id).toBe("runtime.export"); }); });