import { describe, test, expect } from "bun:test"; import { Hono } from "hono"; import { apiResponse } from "../../src/modules/api-utils/apiResponse"; import { ZodError } from "zod"; import GenericError from "../../src/Errors/GenericError"; /** * Tests the error-handling middleware registered in server.ts. * We replicate the onError logic on a fresh Hono instance to test * in isolation without importing all routes. */ function createAppWithErrorHandling() { const app = new Hono(); app.onError((err, ctx) => { const errClassName = err.constructor.name; if ( errClassName.toLowerCase().includes("prisma") || err.message.toLowerCase().includes("prisma") || err.name.toLowerCase().includes("prisma") ) { return ctx.json(apiResponse.internalError(), 500); } if (err instanceof ZodError || err.name === "ZodError") { const zodResp = apiResponse.zodError(err as ZodError); return ctx.json(zodResp, zodResp.status as any); } const response = apiResponse.error(err); return ctx.json(response, response.status); }); return app; } describe("Server error handling", () => { test("Prisma errors return 500 InternalServerError", async () => { const app = createAppWithErrorHandling(); app.get("/test", () => { const err = new Error("prisma query failed"); throw err; }); const res = await app.request("/test"); expect(res.status).toBe(500); const body: any = await res.json(); expect(body.error).toBe("InternalServerError"); expect(body.successful).toBe(false); }); test("Prisma errors detected by class name", async () => { const app = createAppWithErrorHandling(); app.get("/test", () => { class PrismaClientError extends Error { constructor() { super("something"); this.name = "PrismaClientError"; } } throw new PrismaClientError(); }); const res = await app.request("/test"); expect(res.status).toBe(500); }); test("ZodError returns 400 with error array", async () => { const app = createAppWithErrorHandling(); app.get("/test", (c) => { // In Zod v4, we need to use z.parse to generate a proper ZodError const { z } = require("zod"); const schema = z.object({ name: z.string() }); schema.parse({}); // throws ZodError return c.text("unreachable"); }); const res = await app.request("/test"); expect(res.status).toBe(400); const body: any = await res.json(); expect(body.message).toBe("TypeError"); expect(body.successful).toBe(false); expect(Array.isArray(body.error)).toBe(true); }); test("GenericError returns custom status", async () => { const app = createAppWithErrorHandling(); app.get("/test", () => { throw new GenericError({ name: "NotFound", message: "Resource not found", status: 404, }); }); const res = await app.request("/test"); expect(res.status).toBe(404); const body: any = await res.json(); expect(body.error).toBe("NotFound"); expect(body.message).toBe("Resource not found"); expect(body.successful).toBe(false); }); test("plain Error defaults to 400", async () => { const app = createAppWithErrorHandling(); app.get("/test", () => { throw new Error("Unexpected error"); }); const res = await app.request("/test"); expect(res.status).toBe(400); const body: any = await res.json(); expect(body.successful).toBe(false); }); });