import { assert, describe, it } from "@effect/vitest" import { Effect } from "effect" import * as Reactivity from "effect/unstable/reactivity/Reactivity" import type * as SqlError from "effect/unstable/sql/SqlError" import { vi } from "vitest" const state: { connectCause: unknown } = { connectCause: null } vi.mock("mysql2", () => ({ createPool: () => ({ query: (_sql: string, cb: (cause: unknown) => void) => cb(state.connectCause), end: (cb: () => void) => cb(), getConnection: (_cb: () => void) => undefined }) })) const connectFailureReason = (cause: unknown) => Effect.gen(function*() { state.connectCause = cause const { MysqlClient } = yield* Effect.promise(() => import("@effect/sql-mysql2")) const error = yield* Effect.flip(MysqlClient.make({})) return error.reason }).pipe( Effect.scoped, Effect.provide(Reactivity.layer) ) const connectFailureReasonTag = (errno: number) => Effect.map(connectFailureReason({ errno }), (reason) => reason._tag) const assertUniqueViolation = (reason: SqlError.SqlErrorReason, constraint: string) => { assert.strictEqual(reason._tag, "UniqueViolation") if (reason._tag === "UniqueViolation") { assert.strictEqual(reason.constraint, constraint) } } describe("MysqlClient SqlError classification", () => { it.effect("maps representative errno codes to reasons", () => Effect.gen(function*() { const cases = [ [1040, "ConnectionError"], [1045, "AuthenticationError"], [1142, "AuthorizationError"], [1064, "SqlSyntaxError"], [1062, "UniqueViolation"], [1048, "ConstraintError"], [1213, "DeadlockError"], [1205, "LockTimeoutError"], [3024, "StatementTimeoutError"] ] as const for (const [errno, expectedTag] of cases) { const tag = yield* connectFailureReasonTag(errno) assert.strictEqual(tag, expectedTag) } })) it.effect("falls back to UnknownError for unmapped errno", () => Effect.gen(function*() { const tag = yield* connectFailureReasonTag(9999) assert.strictEqual(tag, "UnknownError") })) it.effect("classifies duplicate-entry errno 1062 as UniqueViolation", () => Effect.gen(function*() { const reason = yield* connectFailureReason({ errno: 1062 }) assertUniqueViolation(reason, "unknown") })) it.effect("extracts duplicate-entry constraints from structured fields and common messages", () => Effect.gen(function*() { const explicit = yield* connectFailureReason({ errno: 1062, constraint: " users_email_key ", sqlMessage: "Duplicate entry 'user@example.com' for key 'ignored_key'" }) assertUniqueViolation(explicit, "users_email_key") const singleQuoted = yield* connectFailureReason({ errno: 1062, sqlMessage: "Duplicate entry 'user@example.com' for key 'users_email_key'" }) assertUniqueViolation(singleQuoted, "users_email_key") const backtickQuoted = yield* connectFailureReason({ errno: 1062, message: "Duplicate entry '1' for key `PRIMARY`" }) assertUniqueViolation(backtickQuoted, "PRIMARY") const unquoted = yield* connectFailureReason({ errno: 1062, sqlMessage: "Duplicate entry 'user@example.com' for key users_email_key" }) assertUniqueViolation(unquoted, "users_email_key") })) it.effect("uses unknown for blank, missing, malformed, or non-string duplicate-entry metadata", () => Effect.gen(function*() { const missing = yield* connectFailureReason({ errno: 1062 }) assertUniqueViolation(missing, "unknown") const blank = yield* connectFailureReason({ errno: 1062, constraint: " ", sqlMessage: "Duplicate entry 'user@example.com' for key ' '" }) assertUniqueViolation(blank, "unknown") const malformed = yield* connectFailureReason({ errno: 1062, sqlMessage: "Duplicate entry 'user@example.com'" }) assertUniqueViolation(malformed, "unknown") const nonString = yield* connectFailureReason({ errno: 1062, sqlMessage: 1062, message: { text: "Duplicate entry 'user@example.com' for key 'users_email_key'" } }) assertUniqueViolation(nonString, "unknown") })) it.effect("keeps non-unique constraint errno values classified as ConstraintError", () => Effect.gen(function*() { const tag = yield* connectFailureReasonTag(1452) assert.strictEqual(tag, "ConstraintError") })) })