diff --git a/packages/opencode/src/server/projectors.ts b/packages/opencode/src/server/projectors.ts index 87ed254fff..eb85a8017f 100644 --- a/packages/opencode/src/server/projectors.ts +++ b/packages/opencode/src/server/projectors.ts @@ -5,14 +5,7 @@ import { Session } from "@/session" import { SessionTable } from "@/session/session.sql" import { Database, eq } from "@/storage/db" -let initialized = false - export function initProjectors() { - if (initialized) { - return - } - initialized = true - SyncEvent.init({ projectors: sessionProjectors, convertEvent: (type, data) => { diff --git a/packages/opencode/src/storage/db.ts b/packages/opencode/src/storage/db.ts index a940b3a55f..f41a1ecd85 100644 --- a/packages/opencode/src/storage/db.ts +++ b/packages/opencode/src/storage/db.ts @@ -27,16 +27,20 @@ export const NotFoundError = NamedError.create( const log = Log.create({ service: "db" }) export namespace Database { - export const Path = iife(() => { - if (Flag.OPENCODE_DB) { - if (Flag.OPENCODE_DB === ":memory:" || path.isAbsolute(Flag.OPENCODE_DB)) return Flag.OPENCODE_DB - return path.join(Global.Path.data, Flag.OPENCODE_DB) - } + export function getChannelPath() { const channel = Installation.CHANNEL if (["latest", "beta"].includes(channel) || Flag.OPENCODE_DISABLE_CHANNEL_DB) return path.join(Global.Path.data, "opencode.db") const safe = channel.replace(/[^a-zA-Z0-9._-]/g, "-") return path.join(Global.Path.data, `opencode-${safe}.db`) + } + + export const Path = iife(() => { + if (Flag.OPENCODE_DB) { + if (Flag.OPENCODE_DB === ":memory:" || path.isAbsolute(Flag.OPENCODE_DB)) return Flag.OPENCODE_DB + return path.join(Global.Path.data, Flag.OPENCODE_DB) + } + return getChannelPath() }) export type Transaction = SQLiteTransaction<"sync", void> diff --git a/packages/opencode/test/storage/db.test.ts b/packages/opencode/test/storage/db.test.ts index af5ddec365..f6b6055595 100644 --- a/packages/opencode/test/storage/db.test.ts +++ b/packages/opencode/test/storage/db.test.ts @@ -6,14 +6,9 @@ import { Database } from "../../src/storage/db" describe("Database.Path", () => { test("returns database path for the current channel", () => { - const db = process.env["OPENCODE_DB"] - const expected = db - ? path.isAbsolute(db) - ? db - : path.join(Global.Path.data, db) - : ["latest", "beta"].includes(Installation.CHANNEL) - ? path.join(Global.Path.data, "opencode.db") - : path.join(Global.Path.data, `opencode-${Installation.CHANNEL.replace(/[^a-zA-Z0-9._-]/g, "-")}.db`) - expect(Database.Path).toBe(expected) + const expected = ["latest", "beta"].includes(Installation.CHANNEL) + ? path.join(Global.Path.data, "opencode.db") + : path.join(Global.Path.data, `opencode-${Installation.CHANNEL.replace(/[^a-zA-Z0-9._-]/g, "-")}.db`) + expect(Database.getChannelPath()).toBe(expected) }) }) diff --git a/packages/opencode/test/sync/index.test.ts b/packages/opencode/test/sync/index.test.ts index 30a60e7815..f96750d7d9 100644 --- a/packages/opencode/test/sync/index.test.ts +++ b/packages/opencode/test/sync/index.test.ts @@ -1,4 +1,4 @@ -import { describe, test, expect, beforeEach, afterEach } from "bun:test" +import { describe, test, expect, beforeEach, afterEach, afterAll } from "bun:test" import { tmpdir } from "../fixture/fixture" import z from "zod" import { Bus } from "../../src/bus" @@ -8,6 +8,7 @@ import { Database } from "../../src/storage/db" import { EventTable } from "../../src/sync/event.sql" import { Identifier } from "../../src/id/id" import { Flag } from "../../src/flag/flag" +import { initProjectors } from "../../src/server/projectors" const original = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES @@ -37,29 +38,39 @@ function withInstance(fn: () => void | Promise) { } describe("SyncEvent", () => { - SyncEvent.reset() + function setup() { + SyncEvent.reset() - const Created = SyncEvent.define({ - type: "item.created", - version: 1, - aggregate: "id", - schema: z.object({ id: z.string(), name: z.string() }), - }) - const Sent = SyncEvent.define({ - type: "item.sent", - version: 1, - aggregate: "item_id", - schema: z.object({ item_id: z.string(), to: z.string() }), - }) + const Created = SyncEvent.define({ + type: "item.created", + version: 1, + aggregate: "id", + schema: z.object({ id: z.string(), name: z.string() }), + }) + const Sent = SyncEvent.define({ + type: "item.sent", + version: 1, + aggregate: "item_id", + schema: z.object({ item_id: z.string(), to: z.string() }), + }) - SyncEvent.init({ - projectors: [SyncEvent.project(Created, () => {}), SyncEvent.project(Sent, () => {})], + SyncEvent.init({ + projectors: [SyncEvent.project(Created, () => {}), SyncEvent.project(Sent, () => {})], + }) + + return { Created, Sent } + } + + afterAll(() => { + SyncEvent.reset() + initProjectors() }) describe("run", () => { test( "inserts event row", withInstance(() => { + const { Created } = setup() SyncEvent.run(Created, { id: "evt_1", name: "first" }) const rows = Database.use((db) => db.select().from(EventTable).all()) expect(rows).toHaveLength(1) @@ -71,6 +82,7 @@ describe("SyncEvent", () => { test( "increments seq per aggregate", withInstance(() => { + const { Created } = setup() SyncEvent.run(Created, { id: "evt_1", name: "first" }) SyncEvent.run(Created, { id: "evt_1", name: "second" }) const rows = Database.use((db) => db.select().from(EventTable).all()) @@ -82,6 +94,7 @@ describe("SyncEvent", () => { test( "uses custom aggregate field from agg()", withInstance(() => { + const { Sent } = setup() SyncEvent.run(Sent, { item_id: "evt_1", to: "james" }) const rows = Database.use((db) => db.select().from(EventTable).all()) expect(rows).toHaveLength(1) @@ -92,6 +105,7 @@ describe("SyncEvent", () => { test( "emits events", withInstance(async () => { + const { Created } = setup() const events: Array<{ type: string properties: { id: string; name: string }