import { describe, expect, it } from "vitest"; import { cloneToolcraftMediaAssets } from "../state/media-defaults"; import { readMediaAssets } from "../state/persistence-reader-media"; import type { ToolcraftFileAsset } from "../state/types"; import { createToolcraftMediaResourceRef } from "./media-resource-ref"; import { createMemoryToolcraftBinaryAssetRepository } from "./repository/memory-binary-asset-repository"; import { createToolcraftSettingsAttachments, parseToolcraftSettingsAttachments, resolveToolcraftSettingsAsset, } from "./settings-media"; const bytes = new TextEncoder().encode("test data"); const asset: ToolcraftFileAsset = { assetKind: "file", fileName: "data.csv", id: "data", layerId: "data-layer", lifecycle: "ready", mimeType: "text/csv", position: { x: 0, y: 0 }, resourceRef: createToolcraftMediaResourceRef("file", bytes), sourcePaths: ["project/input/data.csv"], sourceTarget: "source.files", }; describe("settings attachment manifest", () => { it("exports paths and stable references without file bytes, and preserves metadata in state/persistence", () => { const cloned = cloneToolcraftMediaAssets([asset]); expect(cloned[0]?.sourcePaths).toEqual(asset.sourcePaths); expect(cloned[0]?.sourcePaths).not.toBe(asset.sourcePaths); expect(readMediaAssets(cloned)?.[0]?.sourcePaths).toEqual( asset.sourcePaths, ); const manifest = createToolcraftSettingsAttachments(cloned); const { sourcePaths, ...snapshot } = asset; expect(manifest).toEqual([{ paths: sourcePaths, asset: snapshot }]); expect(JSON.stringify(manifest)).not.toContain("test data"); expect( parseToolcraftSettingsAttachments(JSON.parse(JSON.stringify(manifest))), ).toEqual([ { paths: asset.sourcePaths, asset: { ...asset, lifecycle: "restoring" } }, ]); }); it("keeps unavailable file paths and treats malformed records independently", () => { const manifest = createToolcraftSettingsAttachments([ { ...asset, lifecycle: "restoring" }, ]); expect(manifest[0]?.paths).toEqual(asset.sourcePaths); const parsed = parseToolcraftSettingsAttachments([ null, { paths: ["missing.png"], asset: { broken: true } }, ...manifest, ]); expect(parsed).toHaveLength(3); expect(parsed[0]?.asset).toBeNull(); expect(parsed[1]).toEqual({ paths: ["missing.png"], asset: null }); expect(parsed[2]?.asset?.id).toBe(asset.id); }); it("resolves only matching repository bytes and skips missing or corrupt files", async () => { const repository = createMemoryToolcraftBinaryAssetRepository(); expect(await resolveToolcraftSettingsAsset(asset, repository)).toBeNull(); const lease = await repository.beginLease("settings-test"); await lease.put(asset.resourceRef, bytes, { contentType: asset.mimeType, durable: true, }); await lease.commit(); expect( await resolveToolcraftSettingsAsset( { ...asset, lifecycle: "restoring" }, repository, ), ).toEqual(asset); expect( await resolveToolcraftSettingsAsset( { ...asset, resourceRef: "file:///tmp/data.csv" }, repository, ), ).toBeNull(); const corrupt = createMemoryToolcraftBinaryAssetRepository(); const corruptLease = await corrupt.beginLease("corrupt"); await corruptLease.put(asset.resourceRef, new Uint8Array([0]), { contentType: asset.mimeType, durable: true, }); await corruptLease.commit(); expect(await resolveToolcraftSettingsAsset(asset, corrupt)).toBeNull(); }); });