120 lines · 4.4 KB · TypeScript
RawDownload
import { assert, it } from "@effect/vitest";import * as NodeSqliteClient from "@t3tools/shared/nodeSqliteClient";import * as Effect from "effect/Effect";import * as Schema from "effect/Schema";import * as SqlClient from "effect/unstable/sql/SqlClient";import { classifySqliteError, SqlError } from "effect/unstable/sql/SqlError"; import { PersistenceDecodeError, PersistenceSqlError, toPersistenceSqlError } from "./Errors.ts"; const decodeRuntimePayload = Schema.decodeUnknownEffect(  Schema.Struct({    runtimePayload: Schema.Struct({      attempt: Schema.Number,    }),  }),); it("keeps SQL operation context without a tautological detail", () => {  const cause = new Error("database unavailable");  const error = new PersistenceSqlError({    operation: "AuthSessionRepository.list:query",    cause,  });   assert.equal(error.operation, "AuthSessionRepository.list:query");  assert.equal(error.detail, undefined);  assert.equal(error.cause, cause);  assert.equal(error.message, "SQL error in AuthSessionRepository.list:query");}); it.effect("names a real SQLite condition without copying query data", () =>  Effect.gen(function* () {    const sql = yield* SqlClient.SqlClient;    const payload = "sql-private-sentinel";    yield* sql`CREATE TABLE error_test (private_column TEXT PRIMARY KEY)`;    yield* sql`INSERT INTO error_test VALUES (${payload})`;    const cause = yield* Effect.flip(sql`INSERT INTO error_test VALUES (${payload})`);    const error = toPersistenceSqlError("OrchestrationCommandReceiptRepository.upsert:query")(      cause,    );     assert.equal(error.detail, "SQLITE(1555) constraint failed");    assert.equal(error.cause, cause);    assert.notInclude(error.message, payload);    assert.notInclude(error.message, "private_column");  }).pipe(Effect.provide(NodeSqliteClient.layerMemory())),); it("reads the condition through a wrapping driver error", () => {  const driver = Object.assign(new Error("locked"), { errcode: 5, errstr: "database is locked" });  const error = toPersistenceSqlError("AuthSessionRepository.list:query")(    new Error("Failed to prepare statement", { cause: driver }),  );   assert.equal(error.detail, "SQLITE(5) database is locked");}); it.each([{ errno: 1555, code: "SQLITE_CONSTRAINT_PRIMARYKEY" }, { errno: 1 }])(  "names Bun SQLite condition $errno through the SQL error wrapper",  (condition) => {    const driver = Object.assign(new Error("bun-sql-private-sentinel"), {      name: "SQLiteError",      ...condition,    });    const cause = new SqlError({ reason: classifySqliteError(driver) });    const error = toPersistenceSqlError("AuthSessionRepository.create:query")(cause);     assert.equal(      error.message,      `SQL error in AuthSessionRepository.create:query: SQLITE(${condition.errno})`,    );    assert.equal(error.cause, cause);  },); it.each([  new Error("unhelpful"),  Object.assign(new Error("file not found"), { errno: -2, code: "ENOENT" }),])("omits a detail for a cause it cannot categorize (%#)", (cause) => {  const error = toPersistenceSqlError("AuthSessionRepository.list:query")(cause);   assert.equal(error.detail, undefined);  assert.equal(error.message, "SQL error in AuthSessionRepository.list:query");}); it.effect("summarizes a schema cause by issue tag instead of by rejected value", () =>  Effect.gen(function* () {    const rejectedPayload = "sql-mapper-secret-sentinel";    const cause = yield* Effect.flip(      decodeRuntimePayload({ runtimePayload: { attempt: rejectedPayload } }),    );    const error = toPersistenceSqlError("ProviderSessionRuntimeRepository.list:query")(cause);     assert.ok(error.detail !== undefined);    assert.ok(!error.message.includes(rejectedPayload));  }),); it.effect("maps schema errors without copying rejected payloads into diagnostics", () =>  Effect.gen(function* () {    const rejectedPayload = "runtime-payload-secret-sentinel";    const cause = yield* Effect.flip(      decodeRuntimePayload({        runtimePayload: {          attempt: rejectedPayload,        },      }),    );    const error = PersistenceDecodeError.fromSchemaError(      "ProviderSessionRuntimeRepository.list:decodeRows",      cause,    );     assert.equal(error.operation, "ProviderSessionRuntimeRepository.list:decodeRows");    assert.equal(error.cause, cause);    assert.notInclude(error.issue, rejectedPayload);    assert.notInclude(error.message, rejectedPayload);    assert.include(error.issue, "InvalidType");  }),);