Skip to content
Source
Sign inSign up
Open History to see the last commit

SqlErrorClassification.test.ts

100 lines3.3 KB

1import { assert, describe, it } from "@effect/vitest"
2import { Effect } from "effect"
3import * as Reactivity from "effect/reactivity/Reactivity"
4import { SqlError } from "effect/sql/SqlError"
5import { vi } from "vitest"
6​
7const state: {
8 connectCause: unknown
9 queryCause: unknown
10 resultJsonCause: unknown
11} = {
12 connectCause: null,
13 queryCause: null,
14 resultJsonCause: null
16​
17vi.mock("@clickhouse/client", () => ({
18 createClient: () => ({
19 ping: () =>
20 state.connectCause
21 ? Promise.resolve({ success: false, error: state.connectCause })
22 : Promise.resolve({ success: true }),
23 close: () => Promise.resolve(),
24 query: () =>
25 state.queryCause
26 ? Promise.reject(state.queryCause)
27 : Promise.resolve({
28 json: () => state.resultJsonCause ? Promise.reject(state.resultJsonCause) : Promise.resolve({ data: [] })
29 }),
30 command: () => Promise.resolve({}),
31 insert: () => Promise.resolve({})
32 })
33}))
34​
35const connectFailureReasonTag = Effect.fnUntraced(
36 function*(code: number) {
37 state.connectCause = { code }
38 state.queryCause = null
39 state.resultJsonCause = null
40 const { ClickhouseClient } = yield* Effect.promise(() => import("@effect/sql-clickhouse"))
41 const error = yield* Effect.flip(ClickhouseClient.make({ url: "http://localhost:8123" }))
42 return error.reason._tag
43 },
44 (_) => Effect.scoped(_),
45 Effect.provide(Reactivity.layer)
47​
48const queryFailureReasonTag = Effect.fnUntraced(
49 function*(code: number) {
50 state.connectCause = null
51 state.queryCause = { code }
52 state.resultJsonCause = null
53 const { ClickhouseClient } = yield* Effect.promise(() => import("@effect/sql-clickhouse"))
54 const client = yield* ClickhouseClient.make({ url: "http://localhost:8123" })
55 const error = yield* Effect.flip(client`SELECT 1`)
56 return error.reason._tag
57 },
58 (_) => Effect.scoped(_),
59 Effect.provide(Reactivity.layer)
61​
62describe("ClickhouseClient SqlError classification", { concurrent: false }, () => {
63 it.effect("maps representative native codes to reasons", () =>
64 Effect.gen(function*() {
65 const cases = [
66 [516, "AuthenticationError"],
67 [497, "AuthorizationError"],
68 [62, "SqlSyntaxError"],
69 [159, "StatementTimeoutError"]
70 ] as const
71​
72 for (const [code, expectedTag] of cases) {
73 const tag = yield* connectFailureReasonTag(code)
74 assert.strictEqual(tag, expectedTag)
75 }
76 }))
77​
78 it.effect("falls back to UnknownError for unmapped codes during execution", () =>
79 Effect.gen(function*() {
80 const tag = yield* queryFailureReasonTag(999)
81 assert.strictEqual(tag, "UnknownError")
82 }))
83​
84 it.effect("maps result decoding failures to SqlError", () =>
85 Effect.gen(function*() {
86 state.connectCause = null
87 state.queryCause = null
88 state.resultJsonCause = new SyntaxError("invalid JSON response")
89 const { ClickhouseClient } = yield* Effect.promise(() => import("@effect/sql-clickhouse"))
90 const client = yield* ClickhouseClient.make({ url: "http://localhost:8123" })
91 const error = yield* Effect.flip(client`SELECT 1`)
92​
93 assert(error instanceof SqlError)
94 assert.strictEqual(error.reason._tag, "UnknownError")
95 assert.strictEqual(error.reason.cause, state.resultJsonCause)
96 }).pipe(
97 Effect.scoped,
98 Effect.provide(Reactivity.layer)
99 ))
100})