Something went wrong. Try again.
opencode v2 mirror
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186import { beforeAll, beforeEach, describe, expect, mock, test } from "bun:test"import { ServerScope } from "@/runtime/server/scope"
type PersistTestingType = typeof import("./storage").PersistTestingtype PersistType = typeof import("./storage").Persisttype RemovePersistedType = typeof import("./storage").removePersisted
class MemoryStorage implements Storage { private values = new Map<string, string>() readonly events: string[] = [] readonly calls = { get: 0, set: 0, remove: 0 }
clear() { this.values.clear() }
get length() { return this.values.size }
key(index: number) { return Array.from(this.values.keys())[index] ?? null }
getItem(key: string) { this.calls.get += 1 this.events.push(`get:${key}`) if (key.startsWith("opencode.throw")) throw new Error("storage get failed") return this.values.get(key) ?? null }
setItem(key: string, value: string) { this.calls.set += 1 this.events.push(`set:${key}`) if (key.startsWith("opencode.quota")) throw new DOMException("quota", "QuotaExceededError") if (key.startsWith("opencode.throw")) throw new Error("storage set failed") this.values.set(key, value) }
removeItem(key: string) { this.calls.remove += 1 this.events.push(`remove:${key}`) if (key.startsWith("opencode.throw")) throw new Error("storage remove failed") this.values.delete(key) }}
const storage = new MemoryStorage()
let persistTesting: PersistTestingTypelet Persist: PersistTypelet removePersisted: RemovePersistedType
beforeAll(async () => { mock.module("@/runtime/platform/platform", () => ({ usePlatform: () => ({ platform: "web" }), }))
const mod = await import("./storage") persistTesting = mod.PersistTesting Persist = mod.Persist removePersisted = mod.removePersisted})
beforeEach(() => { storage.clear() storage.events.length = 0 storage.calls.get = 0 storage.calls.set = 0 storage.calls.remove = 0 Object.defineProperty(globalThis, "localStorage", { value: storage, configurable: true, })})
describe("persist localStorage resilience", () => { test("does not cache values as persisted when quota write and eviction fail", () => { const storageApi = persistTesting.localStorageWithPrefix("opencode.quota.scope") storageApi.setItem("value", '{"value":1}')
expect(storage.getItem("opencode.quota.scope:value")).toBeNull() expect(storageApi.getItem("value")).toBeNull() })
test("disables only the failing scope when storage throws", () => { const bad = persistTesting.localStorageWithPrefix("opencode.throw.scope") bad.setItem("value", '{"value":1}')
const before = storage.calls.set bad.setItem("value", '{"value":2}') expect(storage.calls.set).toBe(before) expect(bad.getItem("value")).toBeNull()
const healthy = persistTesting.localStorageWithPrefix("opencode.safe.scope") healthy.setItem("value", '{"value":3}') expect(storage.getItem("opencode.safe.scope:value")).toBe('{"value":3}') })
test("failing fallback scope does not poison direct storage scope", () => { const broken = persistTesting.localStorageWithPrefix("opencode.throw.scope2") broken.setItem("value", '{"value":1}')
const direct = persistTesting.localStorageDirect() direct.setItem("direct-value", '{"value":5}')
expect(storage.getItem("direct-value")).toBe('{"value":5}') })
test("workspace storage sanitizes Windows filename characters", () => { const result = persistTesting.workspaceStorage("C:\\Users\\foo")
expect(result).toStartWith("opencode.workspace.") expect(result.endsWith(".dat")).toBeTrue() expect(/[:\\/]/.test(result)).toBeFalse() })
test("workspace target keeps raw path storage as a relocation source", () => { const target = Persist.workspace("C:\\Users\\foo", "vcs")
expect(target.storage).toBe(persistTesting.workspaceStorage("C:/Users/foo")) expect(target.workspaceStorageAliases).toEqual([persistTesting.workspaceStorage("C:\\Users\\foo")]) })
test("workspace target keeps a backslash alias for normalized Windows paths", () => { const target = Persist.workspace("C:/Users/foo", "vcs")
expect(target.storage).toBe(persistTesting.workspaceStorage("C:/Users/foo")) expect(target.workspaceStorageAliases).toEqual([persistTesting.workspaceStorage("C:\\Users\\foo")]) })
test("removes workspace storage aliases when removing persisted target", () => { const target = Persist.workspace("C:\\Users\\foo", "terminal") storage.setItem(`${target.storage}:${target.key}`, '{"value":1}') storage.setItem(`${target.workspaceStorageAliases![0]}:${target.key}`, '{"value":2}')
removePersisted(target)
expect(storage.getItem(`${target.storage}:${target.key}`)).toBeNull() expect(storage.getItem(`${target.workspaceStorageAliases![0]}:${target.key}`)).toBeNull() })
test("draft target isolates storage per draft and namespaces keys", () => { const a = Persist.draft("draft-a", "prompt") const b = Persist.draft("draft-b", "prompt")
expect(a.key).toBe("draft:prompt") expect(a.storage).not.toBe(b.storage) expect(a.storage).not.toBe(Persist.workspace("/home/luke/repo", "prompt").storage) })
test("removes draft storage when removing persisted target", () => { const target = Persist.draft("draft-a", "prompt") storage.setItem(`${target.storage}:${target.key}`, '{"value":1}')
removePersisted(target)
expect(storage.getItem(`${target.storage}:${target.key}`)).toBeNull() })
test("server workspace target preserves local storage and isolates remote storage", () => { const local = Persist.serverWorkspace(ServerScope.local, "/home/luke/repo", "prompt") const windows = Persist.serverWorkspace("https://windows.example" as ServerScope, "/home/luke/repo", "prompt") const debian = Persist.serverWorkspace("https://debian.example" as ServerScope, "/home/luke/repo", "prompt")
expect(local).toEqual(Persist.workspace("/home/luke/repo", "prompt")) expect(windows.storage).not.toBe(local.storage) expect(debian.storage).not.toBe(local.storage) expect(debian.storage).not.toBe(windows.storage) expect(windows.workspaceStorageAliases).toBeUndefined() expect(debian.workspaceStorageAliases).toBeUndefined() })
test("server global target preserves local key and isolates remote keys", () => { expect(Persist.serverGlobal(ServerScope.local, "notification")).toEqual(Persist.global("notification")) expect(Persist.serverGlobal("https://debian.example" as ServerScope, "notification")).toEqual({ storage: "opencode.global.dat", key: "https://debian.example\0notification", }) })
test("server global target cannot collide when scope and key contain colons", () => { expect(Persist.serverGlobal("a:b" as ServerScope, "c")).not.toEqual(Persist.serverGlobal("a" as ServerScope, "b:c")) })})