1import { assert, describe, it } from "@effect/vitest"
2import { Cause, Effect, Exit } from "effect"
3import { Reactivity } from "effect/reactivity"
4import { ConnectionError, SqlError } from "effect/sql/SqlError"
5
6// The wa-sqlite loader fetches its wasm from a file URL, which Node's fetch
7// does not support.
8const isBun = "bun" in process.versions
9
10describe("Memory", () => {
11 it.effect.skipIf(!isBun)("export does not bypass failed-commit cleanup", () =>
12 Effect.gen(function*() {
13 const { SqliteClient } = yield* Effect.promise(() => import("@effect/sql-sqlite-wasm"))
14 const sql = yield* SqliteClient.makeMemory({})
15 yield* sql`PRAGMA foreign_keys = ON`
16 yield* sql`CREATE TABLE parent (id INTEGER PRIMARY KEY)`
17 yield* sql`CREATE TABLE child (parent_id INTEGER REFERENCES parent(id) DEFERRABLE INITIALLY DEFERRED)`
18
19 // Fail only the cleanup ROLLBACK, leaving the deferred constraint failure real.
20 const conn = yield* Effect.scoped(sql.reserve)
21 const executeUnprepared = conn.executeUnprepared
22 let failRollback = true
23 Object.defineProperty(conn, "executeUnprepared", {
24 configurable: true,
25 value: (...args: Parameters<typeof executeUnprepared>) =>
26 args[0] === "ROLLBACK" && failRollback
27 ? Effect.fail(
28 new SqlError({
29 reason: new ConnectionError({
30 message: "injected rollback failure",
31 operation: "rollback",
32 cause: new Error("injected rollback failure")
33 })
34 })
35 )
36 : executeUnprepared.apply(conn, args)
37 })
38 const failedCommit = yield* Effect.exit(sql.withTransaction(sql`INSERT INTO child VALUES (999)`))
39 assert.isTrue(Exit.isFailure(failedCommit))
40
41 const rejected = yield* Effect.exit(sql.export)
42 assert.isTrue(Exit.isFailure(rejected), "export bypassed the rejected connection")
43 if (Exit.isFailure(rejected)) {
44 assert.match(Cause.pretty(rejected.cause), /cannot be reused after failed COMMIT cleanup/i)
45 }
46
47 failRollback = false
48 const snapshot = yield* SqliteClient.makeMemory({})
49 yield* snapshot.import(yield* sql.export)
50 assert.deepStrictEqual(yield* snapshot`SELECT * FROM child`, [])
51 }).pipe(Effect.provide(Reactivity.layer)))
52})