Buckets:
| import { mkdir, mkdtemp, rm } from "node:fs/promises" | |
| import { tmpdir } from "node:os" | |
| import { join } from "node:path" | |
| import { Database } from "bun:sqlite" | |
| import { expect, test } from "bun:test" | |
| import { SqliteClient } from "@effect/sql-sqlite-bun" | |
| import { eq, sql } from "drizzle-orm" | |
| import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core" | |
| import { Effect } from "effect" | |
| import type { SqlClient as SqlClientService } from "effect/unstable/sql/SqlClient" | |
| import { isSqlError } from "effect/unstable/sql/SqlError" | |
| import { EffectDrizzleSqlite } from "../src" | |
| const users = sqliteTable("users", { | |
| id: integer().primaryKey({ autoIncrement: true }), | |
| name: text().notNull(), | |
| }) | |
| const run = <A, E>(effect: Effect.Effect<A, E, SqlClientService>) => | |
| Effect.runPromise( | |
| effect.pipe(Effect.provide(SqliteClient.layer({ filename: ":memory:", disableWAL: true })), Effect.scoped), | |
| ) | |
| const makeDb = Effect.gen(function* () { | |
| const db = yield* EffectDrizzleSqlite.makeWithDefaults() | |
| yield* db.run(sql`create table users (id integer primary key autoincrement, name text not null)`) | |
| return db | |
| }) | |
| const createMigrationsFolder = async () => { | |
| const migrationsFolder = await mkdtemp(join(tmpdir(), "effect-drizzle-sqlite-")) | |
| await mkdir(join(migrationsFolder, "20240101000000_create_migrated_users"), { recursive: true }) | |
| await Bun.write( | |
| join(migrationsFolder, "20240101000000_create_migrated_users", "migration.sql"), | |
| "create table migrated_users (id integer primary key autoincrement, name text not null);", | |
| ) | |
| return migrationsFolder | |
| } | |
| test("selects rows through Effect-yieldable query builders", async () => { | |
| await run( | |
| Effect.gen(function* () { | |
| const db = yield* makeDb | |
| yield* db.insert(users).values({ name: "Ada" }) | |
| expect(yield* db.select().from(users)).toEqual([{ id: 1, name: "Ada" }]) | |
| expect(yield* db.select({ id: users.id }).from(users).where(eq(users.name, "Ada")).get()).toEqual({ id: 1 }) | |
| }), | |
| ) | |
| }) | |
| test("commits successful transactions", async () => { | |
| await run( | |
| Effect.gen(function* () { | |
| const db = yield* makeDb | |
| yield* db.transaction((tx) => tx.insert(users).values({ name: "Grace" }), { behavior: "immediate" }) | |
| expect(yield* db.select().from(users)).toEqual([{ id: 1, name: "Grace" }]) | |
| }), | |
| ) | |
| }) | |
| test("rolls back failed transactions", async () => { | |
| await run( | |
| Effect.gen(function* () { | |
| const db = yield* makeDb | |
| yield* db | |
| .transaction((tx) => | |
| tx | |
| .insert(users) | |
| .values({ name: "Linus" }) | |
| .pipe(Effect.andThen(Effect.fail("boom"))), | |
| ) | |
| .pipe(Effect.ignore) | |
| expect(yield* db.select().from(users)).toEqual([]) | |
| }), | |
| ) | |
| }) | |
| test("rolls back explicit transaction rollback", async () => { | |
| await run( | |
| Effect.gen(function* () { | |
| const db = yield* makeDb | |
| yield* db | |
| .transaction((tx) => | |
| tx | |
| .insert(users) | |
| .values({ name: "Barbara" }) | |
| .pipe(Effect.andThen(Effect.fail(tx.rollback()))), | |
| ) | |
| .pipe(Effect.ignore) | |
| expect(yield* db.select().from(users)).toEqual([]) | |
| }), | |
| ) | |
| }) | |
| test("preserves failed transaction begin errors", async () => { | |
| const dir = await mkdtemp(join(tmpdir(), "effect-drizzle-sqlite-")) | |
| const filename = join(dir, "locked.db") | |
| const holder = new Database(filename) | |
| try { | |
| holder.run("create table users (id integer primary key autoincrement, name text not null)") | |
| holder.run("pragma busy_timeout = 0") | |
| holder.run("begin immediate") | |
| await Effect.runPromise( | |
| Effect.gen(function* () { | |
| const db = yield* EffectDrizzleSqlite.makeWithDefaults() | |
| yield* db.run(sql`pragma busy_timeout = 0`) | |
| const error = yield* db | |
| .transaction((tx) => tx.insert(users).values({ name: "Blocked" }), { behavior: "immediate" }) | |
| .pipe(Effect.flip) | |
| if (!isSqlError(error)) throw new Error("Expected SqlError") | |
| expect(error.reason._tag).toBe("LockTimeoutError") | |
| expect(error.reason.cause instanceof Error ? error.reason.cause.message : "").toContain("database is locked") | |
| }).pipe(Effect.provide(SqliteClient.layer({ filename, disableWAL: true })), Effect.scoped), | |
| ) | |
| } finally { | |
| if (holder.inTransaction) holder.run("rollback") | |
| holder.close() | |
| await rm(dir, { recursive: true, force: true }) | |
| } | |
| }) | |
| test("supports returning and rejects empty update sets", async () => { | |
| await run( | |
| Effect.gen(function* () { | |
| const db = yield* makeDb | |
| const inserted = yield* db.insert(users).values({ name: "Ada" }).returning({ id: users.id, name: users.name }) | |
| expect(inserted).toEqual([{ id: 1, name: "Ada" }]) | |
| const updated = yield* db.update(users).set({ name: "Grace" }).where(eq(users.id, 1)).returning() | |
| expect(updated).toEqual([{ id: 1, name: "Grace" }]) | |
| const deleted = yield* db.delete(users).where(eq(users.id, 1)).returning({ id: users.id }) | |
| expect(deleted).toEqual([{ id: 1 }]) | |
| expect(() => db.update(users).set({ name: undefined })).toThrow("No values to set") | |
| }), | |
| ) | |
| }) | |
| test("runs migrations once and records migration metadata", async () => { | |
| const migrationsFolder = await createMigrationsFolder() | |
| try { | |
| await run( | |
| Effect.gen(function* () { | |
| const db = yield* EffectDrizzleSqlite.makeWithDefaults() | |
| yield* EffectDrizzleSqlite.migrate(db, { migrationsFolder }) | |
| yield* EffectDrizzleSqlite.migrate(db, { migrationsFolder }) | |
| yield* db.run(sql`insert into migrated_users (name) values ('Margaret')`) | |
| expect(yield* db.all<{ name: string }>(sql`select name from migrated_users`)).toEqual([{ name: "Margaret" }]) | |
| expect(yield* db.all<{ name: string | null }>(sql`select name from __drizzle_migrations`)).toEqual([ | |
| { name: "20240101000000_create_migrated_users" }, | |
| ]) | |
| }), | |
| ) | |
| } finally { | |
| await rm(migrationsFolder, { recursive: true, force: true }) | |
| } | |
| }) | |
Xet Storage Details
- Size:
- 6.03 kB
- Xet hash:
- 23d54747dee6f4e0d96d47c4da1af718e6ab11d6da26155af1841596e9a5789b
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.