import { describe, expect, test } from "bun:test"; import { lstat, mkdir, mkdtemp, readFile, rm, stat, symlink, utimes, writeFile, } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { cleanupExpiredToolOutputs } from "../../src/runtime/opencodeToolOutputCleanup.js"; describe("cleanupExpiredToolOutputs", () => { test("removes only expired regular tool output files", async () => { const directory = await mkdtemp(join(tmpdir(), "opencode-tool-output-cleanup-")); const expired = join(directory, "tool_expired"); const current = join(directory, "tool_current"); const unrelated = join(directory, "keep.txt"); const toolDirectory = join(directory, "tool_directory"); const toolSymlink = join(directory, "tool_symlink"); try { await Promise.all([ writeFile(expired, "expired"), writeFile(current, "current"), writeFile(unrelated, "unrelated"), mkdir(toolDirectory), ]); await symlink(unrelated, toolSymlink); const now = Date.now(); const old = new Date(now - 8 * 24 * 60 * 60 * 1000); await utimes(expired, old, old); const result = await cleanupExpiredToolOutputs( directory, 7 * 24 * 60 * 60 * 1000, now, ); expect(result).toEqual({ removed: 1, scanned: 5 }); await expect(stat(expired)).rejects.toMatchObject({ code: "ENOENT" }); expect(await readFile(current, "utf8")).toBe("current"); expect(await readFile(unrelated, "utf8")).toBe("unrelated"); expect((await stat(toolDirectory)).isDirectory()).toBe(true); expect((await lstat(toolSymlink)).isSymbolicLink()).toBe(true); } finally { await rm(directory, { force: true, recursive: true }); } }); test("treats a missing tool output directory as empty", async () => { const directory = join(tmpdir(), `missing-tool-output-${crypto.randomUUID()}`); await expect(cleanupExpiredToolOutputs(directory, 1_000)).resolves.toEqual({ removed: 0, scanned: 0, }); }); });