mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-08 01:07:06 +03:00
92 lines
3.8 KiB
TypeScript
92 lines
3.8 KiB
TypeScript
// Bundled for Node by sqlite-node.test.ts and run with `node <dir>`, so #sqlite
|
|
// resolves to the node:sqlite client that the standalone Node build ships.
|
|
import { join } from "node:path"
|
|
import { Global } from "@opencode/util/global"
|
|
import { sql } from "drizzle-orm"
|
|
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"
|
|
import { Cause, Effect, Fiber, Layer, References } from "effect"
|
|
import { SqlClient } from "effect/sql"
|
|
import { isSqlError } from "effect/sql/SqlError"
|
|
import { Database } from "../../src/database/database"
|
|
import { sqliteLayer } from "../../src/database/sqlite.node"
|
|
|
|
const typed = sqliteTable("typed", { id: integer().primaryKey(), name: text().notNull() })
|
|
|
|
// Same sweep as sqlite-interrupt.test.ts. It runs here because
|
|
// @effect/sql-sqlite-node cannot load inside `bun test` on macOS.
|
|
const sweep = (queue: (sql: SqlClient.SqlClient) => Effect.Effect<unknown, unknown>) =>
|
|
Effect.gen(function* () {
|
|
const leaked: Array<number> = []
|
|
for (let delay = 0; delay < 31; delay++) {
|
|
yield* Effect.scoped(
|
|
Effect.gen(function* () {
|
|
const sql = yield* SqlClient.SqlClient
|
|
const holder = yield* Effect.forkChild(
|
|
Effect.scoped(Effect.andThen(sql.reserve, Effect.andThen(Effect.yieldNow, Effect.yieldNow))),
|
|
)
|
|
const queued = yield* queue(sql).pipe(
|
|
Effect.provideService(References.MaxOpsBeforeYield, 3),
|
|
Effect.forkChild,
|
|
)
|
|
for (let i = 0; i < delay; i++) yield* Effect.yieldNow
|
|
yield* Fiber.interrupt(queued)
|
|
yield* Fiber.join(holder)
|
|
const next = yield* Effect.forkChild(sql`SELECT 3`, { startImmediately: true })
|
|
if (next.pollUnsafe() === undefined) {
|
|
leaked.push(delay)
|
|
yield* Fiber.interrupt(next)
|
|
}
|
|
}).pipe(Effect.provide(sqliteLayer({ filename: ":memory:", disableWAL: true }))),
|
|
)
|
|
}
|
|
return leaked
|
|
})
|
|
|
|
const dir = process.argv[2]!
|
|
|
|
const program = Effect.gen(function* () {
|
|
const { db } = yield* Database.Service
|
|
const journal = yield* db.get<{ journal_mode: string }>(sql`PRAGMA journal_mode`)
|
|
yield* db.run(sql`CREATE TABLE probe (id TEXT PRIMARY KEY)`)
|
|
yield* db.transaction((tx) =>
|
|
Effect.all([tx.run(sql`INSERT INTO probe (id) VALUES ('a')`), tx.run(sql`INSERT INTO probe (id) VALUES ('b')`)]),
|
|
)
|
|
const error = yield* db.run(sql`INSERT INTO probe (id) VALUES ('a')`).pipe(Effect.flip)
|
|
const cause = Cause.isCause(error.cause) ? Cause.squash(error.cause) : error.cause
|
|
const rows = yield* db.all<{ count: number }>(sql`SELECT count(*) AS count FROM probe`)
|
|
|
|
yield* db.run(sql`CREATE TABLE typed (id integer PRIMARY KEY, name text NOT NULL)`)
|
|
const inserted = yield* db.insert(typed).values({ id: 1, name: "Ada" }).returning()
|
|
const duplicate = yield* db.insert(typed).values({ id: 1, name: "Bob" }).returning().pipe(Effect.flip)
|
|
const afterFailure = yield* db.select().from(typed)
|
|
const raw = yield* db.get<{ count: number }>(sql`SELECT count(*) AS count FROM typed`)
|
|
|
|
const leakedTransaction = yield* sweep((client) => client.withTransaction(client`SELECT 2`))
|
|
const leakedReserve = yield* sweep((client) => Effect.scoped(Effect.andThen(client.reserve, Effect.yieldNow)))
|
|
return {
|
|
journal: journal?.journal_mode,
|
|
reason: isSqlError(cause) ? cause.reason._tag : "not a SqlError",
|
|
rows: rows[0]?.count,
|
|
inserted,
|
|
duplicate: duplicate._tag,
|
|
afterFailure,
|
|
raw: raw?.count,
|
|
leakedTransaction,
|
|
leakedReserve,
|
|
}
|
|
})
|
|
|
|
const global = Global.layerWith({
|
|
data: dir,
|
|
cache: dir,
|
|
config: dir,
|
|
state: dir,
|
|
tmp: dir,
|
|
bin: dir,
|
|
log: dir,
|
|
repos: dir,
|
|
})
|
|
|
|
const database = Database.layer({ path: join(dir, "opencode.db") }).pipe(Layer.provide(global))
|
|
console.log(JSON.stringify(await Effect.runPromise(program.pipe(Effect.provide(database)))))
|