| 1 | import { assert, describe, it } from "@effect/vitest" |
| 2 | import * as Cause from "effect/Cause" |
| 3 | import * as Effect from "effect/Effect" |
| 4 | import * as Option from "effect/Option" |
| 5 | import * as Schema from "effect/Schema" |
| 6 | import * as SqlSchema from "effect/sql/SqlSchema" |
| 7 | |
| 8 | describe("SqlSchema", () => { |
| 9 | describe("findAll", () => { |
| 10 | it.effect("returns decoded results", () => |
| 11 | Effect.gen(function*() { |
| 12 | const query = SqlSchema.findAll({ |
| 13 | Request: Schema.NumberFromString, |
| 14 | Result: Schema.Struct({ value: Schema.String }), |
| 15 | execute: (request) => Effect.succeed([{ value: `id:${request}` }]) |
| 16 | }) |
| 17 | |
| 18 | const result = yield* query(1) |
| 19 | assert.deepStrictEqual(result, [{ value: "id:1" }]) |
| 20 | })) |
| 21 | |
| 22 | it.effect("returns empty array when no rows", () => |
| 23 | Effect.gen(function*() { |
| 24 | const query = SqlSchema.findAll({ |
| 25 | Request: Schema.String, |
| 26 | Result: Schema.String, |
| 27 | execute: () => Effect.succeed([]) |
| 28 | }) |
| 29 | |
| 30 | const result = yield* query("test") |
| 31 | assert.deepStrictEqual(result, []) |
| 32 | })) |
| 33 | |
| 34 | it.effect("fails when a row cannot be decoded", () => |
| 35 | Effect.gen(function*() { |
| 36 | const query = SqlSchema.findAll({ |
| 37 | Request: Schema.String, |
| 38 | Result: Schema.Struct({ id: Schema.Number }), |
| 39 | execute: () => Effect.succeed([{ id: "not-a-number" }]) |
| 40 | }) |
| 41 | |
| 42 | const error = yield* Effect.flip(query("ignored")) |
| 43 | assert.isTrue(Schema.isSchemaError(error)) |
| 44 | })) |
| 45 | }) |
| 46 | |
| 47 | describe("findNonEmpty", () => { |
| 48 | it.effect("returns non-empty decoded results", () => |
| 49 | Effect.gen(function*() { |
| 50 | const query = SqlSchema.findNonEmpty({ |
| 51 | Request: Schema.NumberFromString, |
| 52 | Result: Schema.Struct({ value: Schema.String }), |
| 53 | execute: (request) => Effect.succeed([{ value: `id:${request}` }]) |
| 54 | }) |
| 55 | |
| 56 | const result = yield* query(1) |
| 57 | assert.deepStrictEqual(result, [{ value: "id:1" }]) |
| 58 | })) |
| 59 | |
| 60 | it.effect("fails with NoSuchElementError when no rows", () => |
| 61 | Effect.gen(function*() { |
| 62 | const query = SqlSchema.findNonEmpty({ |
| 63 | Request: Schema.String, |
| 64 | Result: Schema.String, |
| 65 | execute: () => Effect.succeed([]) |
| 66 | }) |
| 67 | |
| 68 | const error = yield* Effect.flip(query("test")) |
| 69 | assert.isTrue(Cause.isNoSuchElementError(error)) |
| 70 | })) |
| 71 | }) |
| 72 | |
| 73 | describe("void", () => { |
| 74 | it.effect("encodes request and discards result", () => |
| 75 | Effect.gen(function*() { |
| 76 | let captured: string | undefined |
| 77 | const query = SqlSchema.void({ |
| 78 | Request: Schema.NumberFromString, |
| 79 | execute: (request) => { |
| 80 | captured = request |
| 81 | return Effect.succeed(undefined) |
| 82 | } |
| 83 | }) |
| 84 | |
| 85 | yield* query(42) |
| 86 | assert.strictEqual(captured, "42") |
| 87 | })) |
| 88 | }) |
| 89 | |
| 90 | describe("findOne", () => { |
| 91 | it.effect("returns the first decoded row", () => |
| 92 | Effect.gen(function*() { |
| 93 | const query = SqlSchema.findOne({ |
| 94 | Request: Schema.String, |
| 95 | Result: Schema.NumberFromString, |
| 96 | execute: () => Effect.succeed(["1", "2"]) |
| 97 | }) |
| 98 | |
| 99 | const result = yield* query("ignored") |
| 100 | assert.strictEqual(result, 1) |
| 101 | })) |
| 102 | |
| 103 | it.effect("fails with NoSuchElementError when no rows are returned", () => |
| 104 | Effect.gen(function*() { |
| 105 | const query = SqlSchema.findOne({ |
| 106 | Request: Schema.String, |
| 107 | Result: Schema.String, |
| 108 | execute: () => Effect.succeed([]) |
| 109 | }) |
| 110 | |
| 111 | const error = yield* Effect.flip(query("ignored")) |
| 112 | assert.isTrue(Cause.isNoSuchElementError(error)) |
| 113 | })) |
| 114 | |
| 115 | it.effect("fails when the first row cannot be decoded", () => |
| 116 | Effect.gen(function*() { |
| 117 | const query = SqlSchema.findOne({ |
| 118 | Request: Schema.String, |
| 119 | Result: Schema.Struct({ id: Schema.Number }), |
| 120 | execute: () => Effect.succeed([{ id: "not-a-number" }]) |
| 121 | }) |
| 122 | |
| 123 | const error = yield* Effect.flip(query("ignored")) |
| 124 | assert.isTrue(Schema.isSchemaError(error)) |
| 125 | })) |
| 126 | }) |
| 127 | |
| 128 | describe("findOneOption", () => { |
| 129 | it.effect("returns Option.some when a row exists", () => |
| 130 | Effect.gen(function*() { |
| 131 | const query = SqlSchema.findOneOption({ |
| 132 | Request: Schema.NumberFromString, |
| 133 | Result: Schema.Struct({ value: Schema.String }), |
| 134 | execute: (request) => Effect.succeed([{ value: `id:${request}` }]) |
| 135 | }) |
| 136 | |
| 137 | const result = yield* query(1) |
| 138 | assert.isTrue(Option.isSome(result)) |
| 139 | if (Option.isSome(result)) { |
| 140 | assert.deepStrictEqual(result.value, { value: "id:1" }) |
| 141 | } |
| 142 | })) |
| 143 | |
| 144 | it.effect("returns Option.none when no rows are returned", () => |
| 145 | Effect.gen(function*() { |
| 146 | const query = SqlSchema.findOneOption({ |
| 147 | Request: Schema.NumberFromString, |
| 148 | Result: Schema.String, |
| 149 | execute: () => Effect.succeed([]) |
| 150 | }) |
| 151 | |
| 152 | const result = yield* query(1) |
| 153 | assert.isTrue(Option.isNone(result)) |
| 154 | })) |
| 155 | |
| 156 | it.effect("fails when the first row cannot be decoded", () => |
| 157 | Effect.gen(function*() { |
| 158 | const query = SqlSchema.findOneOption({ |
| 159 | Request: Schema.String, |
| 160 | Result: Schema.Struct({ id: Schema.Number }), |
| 161 | execute: () => Effect.succeed([{ id: "not-a-number" }]) |
| 162 | }) |
| 163 | |
| 164 | const error = yield* Effect.flip(query("ignored")) |
| 165 | assert.isTrue(Schema.isSchemaError(error)) |
| 166 | })) |
| 167 | }) |
| 168 | }) |