From e8b9a247354f5d78dbd3be067296c049236d7f54 Mon Sep 17 00:00:00 2001 From: iceteaSA <171169159+iceteaSA@users.noreply.github.com> Date: Sat, 19 Sep 2026 00:26:45 +0200 Subject: [PATCH] test(opencode): isolate the custody log sink from the live state file The hermetic suite previously contributed 1989 of 2473 live custody-log rows, including 435 short-lived test PIDs. Add process-level Bun preloads plus the hermetic script override so tests cannot write the operator's sink. --- bunfig.toml | 2 + package.json | 2 +- packages/opencode/bunfig.toml | 2 + packages/opencode/src/tests/preload.ts | 8 +++ .../opencode/src/tests/sink-isolation.test.ts | 71 +++++++++++++++++++ 5 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 bunfig.toml create mode 100644 packages/opencode/bunfig.toml create mode 100644 packages/opencode/src/tests/preload.ts create mode 100644 packages/opencode/src/tests/sink-isolation.test.ts diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..5d20fe5 --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,2 @@ +[test] +preload = ["./packages/opencode/src/tests/preload.ts"] diff --git a/package.json b/package.json index d9ea5d6..f8f3a56 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "build": "cd packages/client && bun run build && cd ../opencode && bun run build", "typecheck": "cd packages/client && bun run build && bun run typecheck && cd ../opencode && bun run typecheck", "test": "bun run build && bun test packages", - "test:hermetic": "bun run build && XDG_RUNTIME_DIR=/nonexistent CLAUSTRUM_SUBC_CONNECTION=/nonexistent/x.json bun test packages" + "test:hermetic": "bun run build && XDG_RUNTIME_DIR=/nonexistent CLAUSTRUM_SUBC_CONNECTION=/nonexistent/x.json CLAUSTRUM_CUSTODY_LOG=off bun test packages" }, "devDependencies": { "@types/bun": "1.3.14", diff --git a/packages/opencode/bunfig.toml b/packages/opencode/bunfig.toml new file mode 100644 index 0000000..cf5d009 --- /dev/null +++ b/packages/opencode/bunfig.toml @@ -0,0 +1,2 @@ +[test] +preload = ["./src/tests/preload.ts"] diff --git a/packages/opencode/src/tests/preload.ts b/packages/opencode/src/tests/preload.ts new file mode 100644 index 0000000..c7a897f --- /dev/null +++ b/packages/opencode/src/tests/preload.ts @@ -0,0 +1,8 @@ +import { mkdtempSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +if (process.env.CLAUSTRUM_CUSTODY_LOG === undefined) { + process.env.CLAUSTRUM_CUSTODY_LOG = "off"; +} +process.env.XDG_STATE_HOME = mkdtempSync(join(tmpdir(), "claustrum-opencode-test-state-")); diff --git a/packages/opencode/src/tests/sink-isolation.test.ts b/packages/opencode/src/tests/sink-isolation.test.ts new file mode 100644 index 0000000..0cf15e9 --- /dev/null +++ b/packages/opencode/src/tests/sink-isolation.test.ts @@ -0,0 +1,71 @@ +import { afterEach, expect, test } from "bun:test"; +import { existsSync, readFileSync, statSync } from "node:fs"; +import { join } from "node:path"; + +import type { ClaustrumClient } from "@cortexkit/claustrum-client"; + +import { createOpencodeClaustrumPlugin } from "../plugin"; +import { sentinel, tombstoneFor } from "../tombstone"; + +const PROVIDER = "deepseek"; +const HANDLE = `ckh_${"a".repeat(43)}`; +const liveLogPath = () => join(process.env.HOME ?? ".", ".local", "state", "cortexkit", "opencode-plugin", "custody.jsonl"); +const savedEnv = new Map(); + +function useEnv(key: string, value: string | undefined) { + if (!savedEnv.has(key)) savedEnv.set(key, process.env[key]); + if (value === undefined) delete process.env[key]; + else process.env[key] = value; +} + +afterEach(() => { + for (const [key, value] of savedEnv) { + if (value === undefined) delete process.env[key]; + else process.env[key] = value; + } + savedEnv.clear(); +}); + +test("a plugin serve without an injected logger does not write the live custody log", async () => { + useEnv("CLAUSTRUM_CUSTODY_LOG", undefined); + const path = liveLogPath(); + const beforeExists = existsSync(path); + const beforeContent = beforeExists ? readFileSync(path, "utf8") : ""; + const beforeMtimeMs = beforeExists ? statSync(path).mtimeMs : undefined; + + const plugin = createOpencodeClaustrumPlugin({ + handleReader: async () => ({ + version: 1, + providers: [{ + provider: PROVIDER, + shape: "api", + serve: "opencode-claustrum", + accounts: [{ label: "main", handle: HANDLE, credential_id: `apikey:${PROVIDER}:main` }], + }], + }), + authReader: async () => ({ [PROVIDER]: tombstoneFor("api", PROVIDER) }), + detect: async () => ({ status: "available" as const, schema: 1, wireVersion: 1, endpoints: [] }), + clientFactory: async () => ({ + getCredential: async () => ({ material: "served-material", recordVersion: 1, expiresAtMs: null }), + reportAuthFailure: async () => {}, + } as unknown as ClaustrumClient), + fetch: (async () => new Response("upstream", { status: 200 })) as unknown as typeof globalThis.fetch, + handleVersionReader: async () => "stable", + }); + const hooks = await plugin({} as never); + const config: { provider: Record }> } = { provider: {} }; + + if (!hooks.config) throw new Error("plugin did not provide config hook"); + await hooks.config(config); + const serve = config.provider[PROVIDER]?.options?.fetch; + expect(typeof serve).toBe("function"); + await (serve as (input: string, init?: RequestInit) => Promise) + ("https://upstream.example/v1/chat", { headers: { Authorization: `Bearer ${sentinel(PROVIDER)}` } }); + + const afterExists = existsSync(path); + expect(afterExists).toBe(beforeExists); + if (beforeExists) { + expect(readFileSync(path, "utf8")).toBe(beforeContent); + if (beforeMtimeMs !== undefined) expect(statSync(path).mtimeMs).toBe(beforeMtimeMs); + } +});