test: add comprehensive tests for isTransientError utility
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 17m44s
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 17m44s
This commit is contained in:
parent
597be6bcae
commit
a91b4c53a9
1 changed files with 199 additions and 0 deletions
199
src/__tests__/errors.test.ts
Normal file
199
src/__tests__/errors.test.ts
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
import { describe, it, expect } from "vitest";
|
||||
import { isTransientError } from "../utils/errors.js";
|
||||
|
||||
describe("isTransientError", () => {
|
||||
describe("null/undefined/empty input", () => {
|
||||
it("returns false for null", () => {
|
||||
expect(isTransientError(null)).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for undefined", () => {
|
||||
expect(isTransientError(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for empty object", () => {
|
||||
expect(isTransientError({})).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("error codes from TRANSIENT_ERRORS set", () => {
|
||||
it("returns true for ECONNRESET", () => {
|
||||
expect(isTransientError({ code: "ECONNRESET" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for ECONNREFUSED", () => {
|
||||
expect(isTransientError({ code: "ECONNREFUSED" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for EPIPE", () => {
|
||||
expect(isTransientError({ code: "EPIPE" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for ETIMEDOUT", () => {
|
||||
expect(isTransientError({ code: "ETIMEDOUT" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for CONNECTION_LOST", () => {
|
||||
expect(isTransientError({ code: "CONNECTION_LOST" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for 57P01 (admin_shutdown)", () => {
|
||||
expect(isTransientError({ code: "57P01" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for 57P02 (crash_shutdown)", () => {
|
||||
expect(isTransientError({ code: "57P02" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for 57P03 (cannot_connect_now)", () => {
|
||||
expect(isTransientError({ code: "57P03" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for 08006 (connection_failure)", () => {
|
||||
expect(isTransientError({ code: "08006" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for 08003 (connection_does_not_exist)", () => {
|
||||
expect(isTransientError({ code: "08003" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for 08001 (sqlclient_unable_to_establish_sqlconnection)", () => {
|
||||
expect(isTransientError({ code: "08001" })).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("message substring matching", () => {
|
||||
it("returns true for 'no available server'", () => {
|
||||
expect(isTransientError({ message: "no available server" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for 'connection terminated'", () => {
|
||||
expect(isTransientError({ message: "connection terminated unexpectedly" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for 'connection refused'", () => {
|
||||
expect(isTransientError({ message: "connection refused by server" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for 'server closed the connection'", () => {
|
||||
expect(isTransientError({ message: "server closed the connection unexpectedly" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for 'timeout expired'", () => {
|
||||
expect(isTransientError({ message: "timeout expired waiting for connection" })).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("case-insensitive message matching", () => {
|
||||
it("returns true for 'No Available Server' (mixed case)", () => {
|
||||
expect(isTransientError({ message: "No Available Server" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for 'CONNECTION TERMINATED' (uppercase)", () => {
|
||||
expect(isTransientError({ message: "CONNECTION TERMINATED" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for 'Connection Refused' (title case)", () => {
|
||||
expect(isTransientError({ message: "Connection Refused" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for 'SERVER CLOSED THE CONNECTION' (uppercase)", () => {
|
||||
expect(isTransientError({ message: "SERVER CLOSED THE CONNECTION" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for 'Timeout Expired' (title case)", () => {
|
||||
expect(isTransientError({ message: "Timeout Expired" })).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("non-transient errors", () => {
|
||||
it("returns false for syntax error", () => {
|
||||
expect(isTransientError({
|
||||
code: "42601",
|
||||
message: "syntax error at or near SELECT"
|
||||
})).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for unique constraint violation", () => {
|
||||
expect(isTransientError({
|
||||
code: "23505",
|
||||
message: "duplicate key value violates unique constraint"
|
||||
})).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for foreign key violation", () => {
|
||||
expect(isTransientError({
|
||||
code: "23503",
|
||||
message: "foreign key constraint violation"
|
||||
})).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for not null violation", () => {
|
||||
expect(isTransientError({
|
||||
code: "23502",
|
||||
message: "null value in column violates not-null constraint"
|
||||
})).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for permission denied", () => {
|
||||
expect(isTransientError({
|
||||
code: "42501",
|
||||
message: "permission denied for table users"
|
||||
})).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("unrelated codes and messages", () => {
|
||||
it("returns false for unrelated error code", () => {
|
||||
expect(isTransientError({ code: "UNKNOWN_ERROR" })).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for unrelated error message", () => {
|
||||
expect(isTransientError({ message: "Something went wrong" })).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for generic database error", () => {
|
||||
expect(isTransientError({
|
||||
code: "P0001",
|
||||
message: "Database operation failed"
|
||||
})).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for application error", () => {
|
||||
expect(isTransientError({
|
||||
message: "Invalid user input"
|
||||
})).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("edge cases", () => {
|
||||
it("returns true when both code and message match", () => {
|
||||
expect(isTransientError({
|
||||
code: "ECONNRESET",
|
||||
message: "connection terminated"
|
||||
})).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true when only code matches", () => {
|
||||
expect(isTransientError({
|
||||
code: "ETIMEDOUT",
|
||||
message: "some other message"
|
||||
})).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true when only message matches", () => {
|
||||
expect(isTransientError({
|
||||
code: "SOME_CODE",
|
||||
message: "no available server to connect"
|
||||
})).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false for error with only unrelated code", () => {
|
||||
expect(isTransientError({ code: "NOTFOUND" })).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for error with empty message", () => {
|
||||
expect(isTransientError({ message: "" })).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue