import { describe, expect, it } from "vitest"; import type { ToolcraftDefaultModelAssetSchema } from "../../schema/types"; import { createToolcraftBinaryAssetRepositoryEntry } from "./binary-asset-repository"; import { expandToolcraftBinaryAssetReachability } from "./binary-asset-reachability"; import { createToolcraftResourceManifest } from "./resource-manifest"; import { collectToolcraftReachableResourceRefs } from "./resource-reachability"; function modelResource(ref: string) { return { activeDocumentRef: `${ref}:active`, analysis: { repairPlanRef: `${ref}:analysis-plan` }, assetKind: "model", originalAnalysis: { repairPlanRef: `${ref}:original-analysis-plan` }, originalDocumentRef: `${ref}:original`, repairPlanRef: `${ref}:direct-plan`, repairedDocumentRef: `${ref}:repaired`, sourceBundleRef: `${ref}:source`, }; } function expectedModelRefs(ref: string): string[] { return [ `${ref}:active`, `${ref}:analysis-plan`, `${ref}:direct-plan`, `${ref}:original`, `${ref}:original-analysis-plan`, `${ref}:repaired`, `${ref}:source`, ]; } describe("Toolcraft resource reachability", () => { it("keeps image and file refs reachable across current and history state", () => { const refs = collectToolcraftReachableResourceRefs({ state: { history: { redo: [ { after: { mediaAssets: [ { assetKind: "file", resourceRef: "media:file:file-digest" }, ], }, before: {}, }, ], undo: [], }, mediaAssets: [ { assetKind: "image", resourceRef: "media:image:image-digest" }, ], schema: { media: { defaultAssets: [] } }, }, }); expect(refs).toEqual( new Set([ "media:file:file-digest", "media:image:image-digest", ]), ); }); it("retains refs from current, defaults, undo, redo, jobs, and persistence", () => { const defaultModelAsset = { assetKind: "model", fileName: "default.glb", sourceFiles: [ { dataUrl: "data:model/gltf-binary;base64,AA==", path: "default.glb", }, ], } satisfies ToolcraftDefaultModelAssetSchema; const refs = collectToolcraftReachableResourceRefs({ activeJobs: [ { ...modelResource("job:model"), manifest: createToolcraftResourceManifest({ canonicalDocumentRefs: ["job:manifest-document"], durableSourceRefs: ["job:manifest-source"], }), stagedResourceRefs: ["job:staged"], }, ], activePersistenceManifest: createToolcraftResourceManifest({ canonicalDocumentRefs: ["manifest:document"], derivedResourceRefs: ["manifest:derived"], durableSourceRefs: ["manifest:source"], }), defaultResourceManifest: createToolcraftResourceManifest({ canonicalDocumentRefs: ["default:document"], durableSourceRefs: ["default:source"], }), defaultResourceRefs: ["default:hydrated"], state: { history: { redo: [ { after: { mediaAssets: [modelResource("redo:after")], nested: { sourceBundleRef: "redo:nested-ignored" }, }, before: { mediaAssets: [modelResource("redo:before")] }, }, ], undo: [ { after: { mediaAssets: [modelResource("undo:after")] }, before: { mediaAssets: [modelResource("undo:before")], nested: { repairPlanRef: "undo:nested-ignored" }, }, }, ], }, mediaAssets: [modelResource("current")], schema: { media: { defaultAssets: [defaultModelAsset], }, }, }, }); const expected = [ ...expectedModelRefs("current"), ...expectedModelRefs("job:model"), ...expectedModelRefs("redo:after"), ...expectedModelRefs("redo:before"), ...expectedModelRefs("undo:after"), ...expectedModelRefs("undo:before"), "job:manifest-document", "job:manifest-source", "job:staged", "default:document", "default:hydrated", "default:source", "manifest:derived", "manifest:document", "manifest:source", ].sort(); expect([...refs]).toEqual(expected); expect(refs.has("redo:nested-ignored")).toBe(false); expect(refs.has("undo:nested-ignored")).toBe(false); }); it("whitelists model resource fields instead of recursively scanning values", () => { const typedArray = new Uint8Array([1, 2, 3]); const refs = collectToolcraftReachableResourceRefs({ activeJobs: [ { stagedResourceRefs: ["job:kept"], worker: { arbitraryRef: "worker:ignored", bytes: typedArray }, }, ], state: { history: { redo: [], undo: [ { after: { arbitraryRef: "history:ignored" }, before: {}, }, ], }, mediaAssets: [ { ...modelResource("model"), bytes: typedArray, object3D: { sourceBundleRef: "three:ignored" }, unrelatedRef: "state:ignored", }, { activeDocumentRef: "not-model:ignored", assetKind: "image", }, ], schema: { media: { defaultAssets: [] } }, }, }); expect([...refs]).toEqual( ["job:kept", ...expectedModelRefs("model")].sort(), ); expect(refs.has("worker:ignored")).toBe(false); expect(refs.has("three:ignored")).toBe(false); expect(refs.has("state:ignored")).toBe(false); expect(refs.has("history:ignored")).toBe(false); expect(refs.has("not-model:ignored")).toBe(false); }); it("retains active presentation documents and their repository dependencies", () => { const roots = collectToolcraftReachableResourceRefs({ activePresentationRefs: ["document:presented"], state: { history: { redo: [], undo: [] }, mediaAssets: [], schema: { media: { defaultAssets: [] } }, }, }); const reachable = expandToolcraftBinaryAssetReachability(roots, [ createToolcraftBinaryAssetRepositoryEntry( "document:presented", new Uint8Array([1]), { contentType: "application/vnd.toolcraft.model-document", dependencies: ["texture:albedo"], durable: false, }, ), createToolcraftBinaryAssetRepositoryEntry( "texture:albedo", new Uint8Array([2]), { contentType: "image/png", durable: false }, ), ]); expect([...reachable]).toEqual([ "document:presented", "texture:albedo", ]); }); });