Procházet zdrojové kódy

fix(test): harden preload cleanup against Windows EBUSY (#14895)

Luke Parker před 1 měsícem
rodič
revize
a292eddeb5

+ 13 - 0
packages/opencode/src/storage/db.ts

@@ -33,6 +33,10 @@ export namespace Database {
 
   type Journal = { sql: string; timestamp: number }[]
 
+  const state = {
+    sqlite: undefined as BunDatabase | undefined,
+  }
+
   function time(tag: string) {
     const match = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/.exec(tag)
     if (!match) return 0
@@ -69,6 +73,7 @@ export namespace Database {
     log.info("opening database", { path: path.join(Global.Path.data, "opencode.db") })
 
     const sqlite = new BunDatabase(path.join(Global.Path.data, "opencode.db"), { create: true })
+    state.sqlite = sqlite
 
     sqlite.run("PRAGMA journal_mode = WAL")
     sqlite.run("PRAGMA synchronous = NORMAL")
@@ -95,6 +100,14 @@ export namespace Database {
     return db
   })
 
+  export function close() {
+    const sqlite = state.sqlite
+    if (!sqlite) return
+    sqlite.close()
+    state.sqlite = undefined
+    Client.reset()
+  }
+
   export type TxOrDb = Transaction | Client
 
   const ctx = Context.create<{

+ 18 - 3
packages/opencode/test/preload.ts

@@ -3,14 +3,29 @@
 import os from "os"
 import path from "path"
 import fs from "fs/promises"
-import fsSync from "fs"
 import { afterAll } from "bun:test"
 
 // Set XDG env vars FIRST, before any src/ imports
 const dir = path.join(os.tmpdir(), "opencode-test-data-" + process.pid)
 await fs.mkdir(dir, { recursive: true })
-afterAll(() => {
-  fsSync.rmSync(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 500 })
+afterAll(async () => {
+  const { Database } = await import("../src/storage/db")
+  Database.close()
+  const busy = (error: unknown) =>
+    typeof error === "object" && error !== null && "code" in error && error.code === "EBUSY"
+  const rm = async (left: number): Promise<void> => {
+    Bun.gc(true)
+    await Bun.sleep(100)
+    return fs.rm(dir, { recursive: true, force: true }).catch((error) => {
+      if (!busy(error)) throw error
+      if (left <= 1) throw error
+      return rm(left - 1)
+    })
+  }
+
+  // Windows can keep SQLite WAL handles alive until GC finalizers run, so we
+  // force GC and retry teardown to avoid flaky EBUSY in test cleanup.
+  await rm(30)
 })
 
 process.env["XDG_DATA_HOME"] = path.join(dir, "share")