@ingeze/api-error
Version:
A TypeScript library for handling HTTP errors in Express, NestJS, and Fastify APIs.
258 lines (257 loc) • 7.18 kB
JavaScript
import { describe, beforeEach, it, expect, jest } from "@jest/globals";
import { ErrorHandler } from "../../errors/index.js";
import { expressErrorMiddleware } from "../../handlers/express.js";
import { ZodError } from "zod";
describe("expressErrorMiddleware", () => {
let req;
let res;
let next;
beforeEach(() => {
req = {};
res = {
status: jest.fn().mockReturnThis(),
json: jest.fn().mockReturnThis(),
headersSent: false
};
next = jest.fn();
});
it("should handle custom ErrorHandler instances", () => {
const customError = new ErrorHandler("Custom error", 400, "CUSTOM_ERROR", { id: 1, info: "Custom error" });
expressErrorMiddleware(customError, req, res, next);
expect(res.status).toHaveBeenCalledWith(400);
expect(res.json).toHaveBeenCalledWith({
result: customError.toJSON(),
data: null,
pagination: null
});
expect(next).not.toHaveBeenCalled();
});
it("should handle generic errors with 500 status", () => {
const genericError = new Error("Something went wrong");
expressErrorMiddleware(genericError, req, res, next);
expect(res.status).toHaveBeenCalledWith(500);
expect(res.json).toHaveBeenCalledWith({
result: {
success: false,
statusCode: 500,
type: "INTERNAL_SERVER_ERROR",
message: "An unexpected error occurred"
},
data: null,
pagination: null
});
expect(next).not.toHaveBeenCalled();
});
it("should handle ZodError with validation formatting", () => {
const issues = [
{
code: "invalid_type",
expected: "string",
received: "undefined",
path: ["email"],
message: "Email is not valid"
},
{
code: "invalid_type",
expected: "string",
received: "undefined",
path: ["password"],
message: "Password is required"
}
];
const zodError = new ZodError(issues);
expressErrorMiddleware(zodError, req, res, next);
expect(res.status).toHaveBeenCalledWith(422);
expect(res.json).toHaveBeenCalledWith({
result: {
success: false,
statusCode: 422,
type: "VALIDATION",
message: "Validation error",
details: {
errors: {
email: ["Email is not valid"],
password: ["Password is required"]
}
}
},
data: null,
pagination: null
});
expect(next).not.toHaveBeenCalled();
});
it("should handle ZodError with empty issues array", () => {
const zodError = new ZodError([]);
expressErrorMiddleware(zodError, req, res, next);
expect(res.status).toHaveBeenCalledWith(422);
expect(res.json).toHaveBeenCalledWith({
result: {
success: false,
statusCode: 422,
type: "VALIDATION",
message: "Validation error",
details: {
errors: {}
}
},
data: null,
pagination: null
});
});
it("should handle ZodError without path property", () => {
const issues = [
{
code: "custom",
path: [],
message: "General validation error"
}
];
const zodError = new ZodError(issues);
expressErrorMiddleware(zodError, req, res, next);
expect(res.status).toHaveBeenCalledWith(422);
expect(res.json).toHaveBeenCalledWith({
result: {
success: false,
statusCode: 422,
type: "VALIDATION",
message: "Validation error",
details: {
errors: {
unknown: ["General validation error"]
}
}
},
data: null,
pagination: null
});
});
it("should call next() when headers are already sent", () => {
const error = new Error("Test error");
res.headersSent = true;
expressErrorMiddleware(error, req, res, next);
expect(next).toHaveBeenCalledWith(error);
expect(res.status).not.toHaveBeenCalled();
expect(res.json).not.toHaveBeenCalled();
});
it("should handle ZodError with multiple errors for same field", () => {
const issues = [
{
code: "too_small",
minimum: 1,
type: "string",
inclusive: true,
exact: false,
path: ["username"],
message: "Username is required"
},
{
code: "too_small",
minimum: 3,
type: "string",
inclusive: true,
exact: false,
path: ["username"],
message: "Username must be at least 3 characters"
}
];
const zodError = new ZodError(issues);
expressErrorMiddleware(zodError, req, res, next);
expect(res.status).toHaveBeenCalledWith(422);
expect(res.json).toHaveBeenCalledWith({
result: {
success: false,
statusCode: 422,
type: "VALIDATION",
message: "Validation error",
details: {
errors: {
username: ["Username is required", "Username must be at least 3 characters"]
}
}
},
data: null,
pagination: null
});
});
it("should handle ZodError with nested path", () => {
const issues = [
{
code: "invalid_type",
expected: "number",
received: "string",
path: ["user", "profile", "age"],
message: "Age must be a number"
}
];
const zodError = new ZodError(issues);
expressErrorMiddleware(zodError, req, res, next);
expect(res.status).toHaveBeenCalledWith(422);
expect(res.json).toHaveBeenCalledWith({
result: {
success: false,
statusCode: 422,
type: "VALIDATION",
message: "Validation error",
details: {
errors: {
user: ["Age must be a number"]
}
}
},
data: null,
pagination: null
});
});
it("should handle mixed ZodError issues correctly", () => {
const issues = [
{
code: "invalid_string",
validation: "email",
path: ["email"],
message: "Invalid email format"
},
{
code: "invalid_type",
expected: "string",
received: "undefined",
path: ["password"],
message: "Password is required"
},
{
code: "custom",
path: ["email"],
message: "Email already exists"
},
{
code: "too_small",
minimum: 1,
type: "number",
inclusive: true,
exact: false,
path: ["age"],
message: "Age must be positive"
}
];
const zodError = new ZodError(issues);
expressErrorMiddleware(zodError, req, res, next);
expect(res.status).toHaveBeenCalledWith(422);
expect(res.json).toHaveBeenCalledWith({
result: {
success: false,
statusCode: 422,
type: "VALIDATION",
message: "Validation error",
details: {
errors: {
email: ["Invalid email format", "Email already exists"],
password: ["Password is required"],
age: ["Age must be positive"]
}
}
},
data: null,
pagination: null
});
});
});
//# sourceMappingURL=express.test.js.map