Merge commit '36c66dd290d3ce6eb1ccd310d0c658d4a32bb8eb' as 'repos/effect'
This commit is contained in:
65
repos/effect/packages/sql/d1/test/Client.test.ts
Normal file
65
repos/effect/packages/sql/d1/test/Client.test.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { D1Client } from "@effect/sql-d1"
|
||||
import { assert, describe, it } from "@effect/vitest"
|
||||
import { Cause, Effect } from "effect"
|
||||
import * as Reactivity from "effect/unstable/reactivity/Reactivity"
|
||||
import { D1Miniflare } from "./utils.ts"
|
||||
|
||||
describe("Client", () => {
|
||||
it.effect("classifies native errors without stable sqlite codes as UnknownError", () =>
|
||||
Effect.gen(function*() {
|
||||
const failingDb = {
|
||||
prepare: () => ({
|
||||
bind: () => ({
|
||||
all: async () => {
|
||||
throw new Error("boom")
|
||||
}
|
||||
})
|
||||
})
|
||||
} as any
|
||||
|
||||
const client = yield* D1Client.make({ db: failingDb })
|
||||
const error = yield* Effect.flip(client`SELECT 1`)
|
||||
assert.strictEqual(error.reason._tag, "UnknownError")
|
||||
}).pipe(
|
||||
Effect.scoped,
|
||||
Effect.provide(Reactivity.layer)
|
||||
))
|
||||
|
||||
it.effect("should handle queries without transactions", () =>
|
||||
Effect.gen(function*() {
|
||||
const sql = yield* D1Client.D1Client
|
||||
yield* sql`CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)`
|
||||
yield* sql`INSERT INTO test (name) VALUES ('hello')`
|
||||
let rows = yield* sql`SELECT * FROM test`
|
||||
assert.deepStrictEqual(rows, [{ id: 1, name: "hello" }])
|
||||
yield* sql`INSERT INTO test (name) VALUES ('world')`
|
||||
rows = yield* sql`SELECT * FROM test`
|
||||
assert.deepStrictEqual(rows, [
|
||||
{ id: 1, name: "hello" },
|
||||
{ id: 2, name: "world" }
|
||||
])
|
||||
}).pipe(Effect.provide(D1Miniflare.layerClient)))
|
||||
|
||||
it.effect("should handle queries with params without transactions", () =>
|
||||
Effect.gen(function*() {
|
||||
const sql = yield* D1Client.D1Client
|
||||
yield* sql`CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)`
|
||||
yield* sql`INSERT INTO test ${sql.insert({ name: "hello" })}`
|
||||
const rows = yield* sql`SELECT * FROM test WHERE name = ${"hello"}`
|
||||
assert.deepStrictEqual(rows, [{ id: 1, name: "hello" }])
|
||||
}).pipe(Effect.provide(D1Miniflare.layerClient)))
|
||||
|
||||
it.effect("should defect on transactions", () =>
|
||||
Effect.gen(function*() {
|
||||
const sql = yield* D1Client.D1Client
|
||||
yield* sql`CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)`
|
||||
const res = yield* sql`INSERT INTO test ${sql.insert({ name: "hello" })}`.pipe(
|
||||
sql.withTransaction,
|
||||
Effect.sandbox,
|
||||
Effect.flip
|
||||
)
|
||||
const rows = yield* sql`SELECT * FROM test`
|
||||
assert.deepStrictEqual(rows, [])
|
||||
assert.equal(Cause.hasDies(res), true)
|
||||
}).pipe(Effect.provide(D1Miniflare.layerClient)))
|
||||
})
|
||||
176
repos/effect/packages/sql/d1/test/Resolver.test.ts
Normal file
176
repos/effect/packages/sql/d1/test/Resolver.test.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
import { D1Client } from "@effect/sql-d1"
|
||||
import { assert, describe, it } from "@effect/vitest"
|
||||
import { Cause, Effect, Iterable } from "effect"
|
||||
import * as Schema from "effect/Schema"
|
||||
import { SqlError, SqlResolver } from "effect/unstable/sql"
|
||||
import { D1Miniflare } from "./utils.ts"
|
||||
|
||||
const seededClient = Effect.gen(function*() {
|
||||
const sql = yield* D1Client.D1Client
|
||||
yield* sql`CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)`
|
||||
for (const id of Iterable.range(1, 100)) {
|
||||
yield* sql`INSERT INTO test ${sql.insert({ id, name: `name${id}` })}`
|
||||
}
|
||||
return sql
|
||||
})
|
||||
|
||||
describe("Resolver", () => {
|
||||
describe("ordered", () => {
|
||||
it.effect("insert", () =>
|
||||
Effect.gen(function*() {
|
||||
const batches: Array<Array<string>> = []
|
||||
const sql = yield* seededClient
|
||||
const Insert = SqlResolver.ordered({
|
||||
Request: Schema.String,
|
||||
Result: Schema.Struct({ id: Schema.Number, name: Schema.String }),
|
||||
execute: (names) => {
|
||||
batches.push(names)
|
||||
return sql`INSERT INTO test ${sql.insert(names.map((name) => ({ name })))} RETURNING *`
|
||||
}
|
||||
})
|
||||
const execute = SqlResolver.request(Insert)
|
||||
assert.deepStrictEqual(
|
||||
yield* Effect.all({
|
||||
one: execute("one"),
|
||||
two: SqlResolver.request("two", Insert)
|
||||
}, { concurrency: "unbounded" }),
|
||||
{
|
||||
one: { id: 101, name: "one" },
|
||||
two: { id: 102, name: "two" }
|
||||
}
|
||||
)
|
||||
assert.deepStrictEqual(batches, [["one", "two"]])
|
||||
}).pipe(Effect.provide(D1Miniflare.layerClient)))
|
||||
|
||||
it.effect("result length mismatch", () =>
|
||||
Effect.gen(function*() {
|
||||
const batches: Array<Array<number>> = []
|
||||
const sql = yield* seededClient
|
||||
const Select = SqlResolver.ordered({
|
||||
Request: Schema.Number,
|
||||
Result: Schema.Struct({ id: Schema.Number, name: Schema.String }),
|
||||
execute: (ids) => {
|
||||
batches.push(ids)
|
||||
return sql`SELECT * FROM test WHERE id IN ${sql.in(ids)}`
|
||||
}
|
||||
})
|
||||
const execute = SqlResolver.request(Select)
|
||||
const error = yield* Effect.all([
|
||||
execute(1),
|
||||
execute(2),
|
||||
execute(3),
|
||||
execute(101)
|
||||
], { concurrency: "unbounded" }).pipe(
|
||||
Effect.flip
|
||||
)
|
||||
assert(error instanceof SqlError.ResultLengthMismatch)
|
||||
assert.strictEqual(error.actual, 3)
|
||||
assert.strictEqual(error.expected, 4)
|
||||
assert.deepStrictEqual(batches, [[1, 2, 3, 101]])
|
||||
}).pipe(Effect.provide(D1Miniflare.layerClient)))
|
||||
})
|
||||
|
||||
describe("grouped", () => {
|
||||
it.effect("find by name", () =>
|
||||
Effect.gen(function*() {
|
||||
const sql = yield* seededClient
|
||||
const FindByName = SqlResolver.grouped({
|
||||
Request: Schema.String,
|
||||
RequestGroupKey: (name) => name,
|
||||
Result: Schema.Struct({ id: Schema.Number, name: Schema.String }),
|
||||
ResultGroupKey: (result) => result.name,
|
||||
execute: (names) => sql`SELECT * FROM test WHERE name IN ${sql.in(names)}`
|
||||
})
|
||||
const execute = SqlResolver.request(FindByName)
|
||||
yield* sql`INSERT INTO test ${sql.insert({ name: "name1" })}`
|
||||
assert.deepStrictEqual(
|
||||
yield* Effect.all({
|
||||
one: execute("name1"),
|
||||
two: execute("name2"),
|
||||
three: Effect.flip(execute("name0"))
|
||||
}, { concurrency: "unbounded" }),
|
||||
{
|
||||
one: [{ id: 1, name: "name1" }, { id: 101, name: "name1" }],
|
||||
two: [{ id: 2, name: "name2" }],
|
||||
three: new Cause.NoSuchElementError()
|
||||
}
|
||||
)
|
||||
}).pipe(Effect.provide(D1Miniflare.layerClient)))
|
||||
|
||||
it.effect("using raw rows", () =>
|
||||
Effect.gen(function*() {
|
||||
const sql = yield* seededClient
|
||||
const FindByName = SqlResolver.grouped({
|
||||
Request: Schema.String,
|
||||
RequestGroupKey: (name) => name,
|
||||
Result: Schema.Struct({ id: Schema.Number, name: Schema.String }),
|
||||
ResultGroupKey: (_, result: any) => result.name,
|
||||
execute: (names) => sql`SELECT * FROM test WHERE name IN ${sql.in(names)}`
|
||||
})
|
||||
const execute = SqlResolver.request(FindByName)
|
||||
yield* sql`INSERT INTO test ${sql.insert({ name: "name1" })}`
|
||||
assert.deepStrictEqual(
|
||||
yield* Effect.all({
|
||||
one: execute("name1"),
|
||||
two: execute("name2"),
|
||||
three: Effect.flip(execute("name0"))
|
||||
}, { concurrency: "unbounded" }),
|
||||
{
|
||||
one: [{ id: 1, name: "name1" }, { id: 101, name: "name1" }],
|
||||
two: [{ id: 2, name: "name2" }],
|
||||
three: new Cause.NoSuchElementError()
|
||||
}
|
||||
)
|
||||
}).pipe(Effect.provide(D1Miniflare.layerClient)))
|
||||
})
|
||||
|
||||
describe("findById", () => {
|
||||
it.effect("find by id", () =>
|
||||
Effect.gen(function*() {
|
||||
const sql = yield* seededClient
|
||||
const FindById = SqlResolver.findById({
|
||||
Id: Schema.Number,
|
||||
Result: Schema.Struct({ id: Schema.Number, name: Schema.String }),
|
||||
ResultId: (result) => result.id,
|
||||
execute: (ids) => sql`SELECT * FROM test WHERE id IN ${sql.in(ids)}`
|
||||
})
|
||||
const execute = SqlResolver.request(FindById)
|
||||
assert.deepStrictEqual(
|
||||
yield* Effect.all({
|
||||
one: execute(1),
|
||||
two: execute(2),
|
||||
three: Effect.flip(execute(101))
|
||||
}, { concurrency: "unbounded" }),
|
||||
{
|
||||
one: { id: 1, name: "name1" },
|
||||
two: { id: 2, name: "name2" },
|
||||
three: new Cause.NoSuchElementError()
|
||||
}
|
||||
)
|
||||
}).pipe(Effect.provide(D1Miniflare.layerClient)))
|
||||
|
||||
it.effect("using raw rows", () =>
|
||||
Effect.gen(function*() {
|
||||
const sql = yield* seededClient
|
||||
const FindById = SqlResolver.findById({
|
||||
Id: Schema.Number,
|
||||
Result: Schema.Struct({ id: Schema.Number, name: Schema.String }),
|
||||
ResultId: (_, result: any) => result.id,
|
||||
execute: (ids) => sql`SELECT * FROM test WHERE id IN ${sql.in(ids)}`
|
||||
})
|
||||
const execute = SqlResolver.request(FindById)
|
||||
assert.deepStrictEqual(
|
||||
yield* Effect.all({
|
||||
one: execute(1),
|
||||
two: execute(2),
|
||||
three: Effect.flip(execute(101))
|
||||
}, { concurrency: "unbounded" }),
|
||||
{
|
||||
one: { id: 1, name: "name1" },
|
||||
two: { id: 2, name: "name2" },
|
||||
three: new Cause.NoSuchElementError()
|
||||
}
|
||||
)
|
||||
}).pipe(Effect.provide(D1Miniflare.layerClient)))
|
||||
})
|
||||
})
|
||||
38
repos/effect/packages/sql/d1/test/utils.ts
Normal file
38
repos/effect/packages/sql/d1/test/utils.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { D1Database } from "@cloudflare/workers-types"
|
||||
import { D1Client } from "@effect/sql-d1"
|
||||
import { Context, Data, Effect, Layer } from "effect"
|
||||
import { Miniflare } from "miniflare"
|
||||
|
||||
export class MiniflareError extends Data.TaggedError("MiniflareError")<{
|
||||
cause: unknown
|
||||
}> {}
|
||||
|
||||
export class D1Miniflare extends Context.Service<
|
||||
D1Miniflare,
|
||||
Miniflare
|
||||
>()("test/D1Miniflare") {
|
||||
static readonly layer = Layer.effect(this)(
|
||||
Effect.acquireRelease(
|
||||
Effect.try({
|
||||
try: () =>
|
||||
new Miniflare({
|
||||
modules: true,
|
||||
d1Databases: {
|
||||
DB: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
},
|
||||
script: ""
|
||||
}),
|
||||
catch: (cause) => new MiniflareError({ cause })
|
||||
}),
|
||||
(miniflare) => Effect.promise(() => miniflare.dispose())
|
||||
)
|
||||
)
|
||||
|
||||
static layerClient = Layer.unwrap(
|
||||
Effect.gen(function*() {
|
||||
const miniflare = yield* D1Miniflare
|
||||
const db: D1Database = yield* Effect.tryPromise(() => miniflare.getD1Database("DB"))
|
||||
return D1Client.layer({ db })
|
||||
})
|
||||
).pipe(Layer.provide(this.layer))
|
||||
}
|
||||
Reference in New Issue
Block a user