import { assertEquals, assertRejects } from "@std/assert"; import { BACKGROUND_CONTEXT as context } from "@earendil-works/chord/context"; import { FileError, getOrThrow } from "@earendil-works/pi-durable/env"; import { tempDir } from "@berry/util"; import { Bundles, MAX_BUNDLE_BYTES } from "./bundles.ts"; import { hostEnvironment, targetEnvironment, writeSkill } from "./testing.ts"; Deno.test("host bundles preserve binary assets, empty directories, and resource-only versions on new targets", async () => { await using home = await tempDir(); await using target = await tempDir(); const host = hostEnvironment(home.path); const main = await writeSkill( host, `${home.path}/.config/berry/skills`, "review", "Read references/checklist.txt.", ); const directory = main.slice(0, -"/SKILL.md".length); getOrThrow( await host.writeFile(`${directory}/references/checklist.txt`, "original checklist", context), ); getOrThrow( await host.writeFile(`${directory}/assets/data.bin`, new Uint8Array([0, 255, 128]), context), ); getOrThrow(await host.createDir(`${directory}/empty`, { recursive: true }, context)); await Deno.symlink("checklist.txt", `${directory}/references/alias.txt`); const bundles = new Bundles(host); const first = await bundles.capture(directory, context); const env = targetEnvironment(target.path); const copied = await bundles.materialize(first.id, env, context); assertEquals( [...getOrThrow(await env.readBinaryFile(`${copied}/assets/data.bin`, context))], [0, 255, 128], ); assertEquals( getOrThrow(await env.readTextFile(`${copied}/references/alias.txt`, context)), "original checklist", ); assertEquals(getOrThrow(await env.fileInfo(`${copied}/empty`, context)).kind, "directory"); getOrThrow( await host.writeFile(`${directory}/references/checklist.txt`, "updated checklist", context), ); const second = await bundles.capture(directory, context); const updated = await bundles.materialize(second.id, env, context); assertEquals(first.content, second.content); assertEquals( getOrThrow(await env.readTextFile(`${updated}/references/checklist.txt`, context)), "updated checklist", ); getOrThrow(await host.remove(directory, { recursive: true }, context)); await using other = await tempDir(); const next = targetEnvironment(other.path); const historical = await new Bundles(hostEnvironment(home.path)).materialize( first.id, next, context, ); assertEquals( getOrThrow(await next.readTextFile(`${historical}/references/checklist.txt`, context)), "original checklist", ); }); Deno.test("bundle capture refuses escaping symlinks and cycles rather than copying unrelated host files", async () => { await using home = await tempDir(); const host = hostEnvironment(home.path); const main = await writeSkill( host, `${home.path}/.config/berry/skills`, "review", "instructions", ); const directory = main.slice(0, -"/SKILL.md".length); await Deno.writeTextFile(`${home.path}/private.txt`, "not part of the skill"); await Deno.symlink(`${home.path}/private.txt`, `${directory}/escape.txt`); const bundles = new Bundles(host); await assertRejects(() => bundles.capture(directory, context), Error, "escapes"); await Deno.remove(`${directory}/escape.txt`); await Deno.symlink(directory, `${directory}/cycle`); await assertRejects(() => bundles.capture(directory, context), Error, "cycle"); }); Deno.test("oversized resource bundles fail before publication", async () => { await using home = await tempDir(); const host = hostEnvironment(home.path); const main = await writeSkill( host, `${home.path}/.config/berry/skills`, "review", "instructions", ); const directory = main.slice(0, -"/SKILL.md".length); await Deno.writeTextFile(`${directory}/large.bin`, ""); await Deno.truncate(`${directory}/large.bin`, MAX_BUNDLE_BYTES + 1); await assertRejects(() => new Bundles(host).capture(directory, context), Error, "size limit"); }); Deno.test("an interrupted copy can be retried concurrently without exposing partial bundles", async () => { await using home = await tempDir(); await using target = await tempDir(); const host = hostEnvironment(home.path); const main = await writeSkill( host, `${home.path}/.config/berry/skills`, "review", "instructions", ); const directory = main.slice(0, -"/SKILL.md".length); getOrThrow( await host.writeFile(`${directory}/references/checklist.txt`, "complete checklist", context), ); const bundles = new Bundles(host); const snapshot = await bundles.capture(directory, context); const env = targetEnvironment(target.path); const write = env.writeFile; env.writeFile = (path, content, context) => path.endsWith("/checklist.txt") ? Promise.resolve({ ok: false, error: new FileError("unknown", "Transfer interrupted") }) : write(path, content, context); await assertRejects( () => bundles.materialize(snapshot.id, env, context), Error, "Transfer interrupted", ); env.writeFile = write; const paths = await Promise.all([ bundles.materialize(snapshot.id, env, context), bundles.materialize(snapshot.id, env, context), ]); assertEquals(paths[0], paths[1]); assertEquals( getOrThrow(await env.readTextFile(`${paths[0]}/references/checklist.txt`, context)), "complete checklist", ); assertEquals( getOrThrow(await env.readTextFile(`${paths[0]}/SKILL.md`, context)), snapshot.content, ); });