Diff
1diff --git a/bunfig.toml b/bunfig.toml
2new file mode 100644
3index 0000000000000000000000000000000000000000..8755352e57a442d69a3fc4db015cc51512cf9373
4--- /dev/null
5+++ b/bunfig.toml
6@@ -0,0 +1,2 @@
7+[test]
8+preload = ["./test/setup.ts"]
9diff --git a/src/cache.ts b/src/cache.ts
10index 975a5f938b139f61b6723a4da360973a57460d73..4d602ff1675f29f70e6fb87bc2e1ca4bfd180e33 100644
11--- a/src/cache.ts
12+++ b/src/cache.ts
13@@ -1,12 +1,26 @@
14-import { mkdirSync, readFileSync, appendFileSync } from "fs"
15-import { existsSync } from "fs"
16+import { mkdirSync, readFileSync, appendFileSync, existsSync } from "fs"
17 import { homedir } from "os"
18-import { join } from "path"
19+import { join, dirname } from "path"
20 import type { Decision } from "./types"
21 import { POLICY_VERSION } from "./rules"
22
23-const CACHE_DIR = join(homedir(), ".config", "opencode", "hooks", "cache")
24-const CACHE_FILE = join(CACHE_DIR, "decisions.jsonl")
25+let cacheFile = join(
26+ homedir(),
27+ ".config",
28+ "opencode",
29+ "hooks",
30+ "cache",
31+ "decisions.jsonl",
32+)
33+let enabled = true
34+
35+export function setCacheEnabled(v: boolean) {
36+ enabled = v
37+}
38+
39+export function setCacheFile(path: string) {
40+ cacheFile = path
41+}
42
43 type CacheEntry = {
44 key: string
45@@ -19,13 +33,15 @@ type CacheEntry = {
46 }
47
48 function ensureDir() {
49- if (!existsSync(CACHE_DIR)) mkdirSync(CACHE_DIR, { recursive: true })
50+ const dir = dirname(cacheFile)
51+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
52 }
53
54 export function lookupCache(key: string): Decision | undefined {
55- if (!existsSync(CACHE_FILE)) return undefined
56+ if (!enabled) return undefined
57+ if (!existsSync(cacheFile)) return undefined
58 try {
59- const data = readFileSync(CACHE_FILE, "utf-8")
60+ const data = readFileSync(cacheFile, "utf-8")
61 for (const line of data.split("\n")) {
62 if (!line) continue
63 try {
64@@ -49,6 +65,7 @@ export function writeCache(
65 decision: Decision,
66 source: string,
67 ): void {
68+ if (!enabled) return
69 ensureDir()
70 const entry: CacheEntry = {
71 key,
72@@ -60,7 +77,7 @@ export function writeCache(
73 source,
74 }
75 try {
76- appendFileSync(CACHE_FILE, JSON.stringify(entry) + "\n")
77+ appendFileSync(cacheFile, JSON.stringify(entry) + "\n")
78 } catch {
79 // non-fatal
80 }
81diff --git a/test/cache.test.ts b/test/cache.test.ts
82index fa6b8df59652faffd496d0b9c51f47900f3f1fea..3e4eecfbe1151d486fa69f22cad353f6c392d38c 100644
83--- a/test/cache.test.ts
84+++ b/test/cache.test.ts
85@@ -1,20 +1,29 @@
86-import { describe, expect, test, beforeEach } from "bun:test"
87-import { lookupCache, writeCache } from "../src/cache"
88-import { unlinkSync, existsSync } from "fs"
89-import { homedir } from "os"
90+import { describe, expect, test, beforeEach, beforeAll, afterAll } from "bun:test"
91+import {
92+ lookupCache,
93+ writeCache,
94+ setCacheEnabled,
95+ setCacheFile,
96+} from "../src/cache"
97+import { existsSync, mkdtempSync, rmSync, unlinkSync } from "fs"
98+import { tmpdir } from "os"
99 import { join } from "path"
100
101-const CACHE_FILE = join(
102- homedir(),
103- ".config",
104- "opencode",
105- "hooks",
106- "cache",
107- "decisions.jsonl",
108-)
109+const TMP_DIR = mkdtempSync(join(tmpdir(), "policy-cache-test-"))
110+const TMP_FILE = join(TMP_DIR, "decisions.jsonl")
111+
112+beforeAll(() => {
113+ setCacheFile(TMP_FILE)
114+ setCacheEnabled(true)
115+})
116+
117+afterAll(() => {
118+ setCacheEnabled(false)
119+ rmSync(TMP_DIR, { recursive: true, force: true })
120+})
121
122 beforeEach(() => {
123- if (existsSync(CACHE_FILE)) unlinkSync(CACHE_FILE)
124+ if (existsSync(TMP_FILE)) unlinkSync(TMP_FILE)
125 })
126
127 describe("cache", () => {
128diff --git a/test/setup.ts b/test/setup.ts
129new file mode 100644
130index 0000000000000000000000000000000000000000..745897339073c14b6e09cec7772ae6c999e286ce
131--- /dev/null
132+++ b/test/setup.ts
133@@ -0,0 +1,7 @@
134+// Test preload: disable filesystem side effects globally.
135+// Individual tests may re-enable + redirect paths for their own scope.
136+import { setCacheEnabled } from "../src/cache"
137+import { setLoggingEnabled } from "../src/logger"
138+
139+setCacheEnabled(false)
140+setLoggingEnabled(false)