import { describe, expect, it, vi } from 'vitest'; import { DatabaseError } from 'pg'; import type { Request, Response } from 'express'; import { errorMiddleware } from '@/api/middlewares/error.js'; import { AppError } from '@/utils/errors.js'; import { ERROR_CODES } from '@insforge/shared-schemas'; vi.mock('@/utils/logger.js', () => ({ default: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn() }, })); function makeMockRes() { const res = {} as Response & { status: ReturnType; json: ReturnType; statusCode?: number; body?: unknown; }; res.status = vi.fn((code: number) => { res.statusCode = code; return res; }); res.json = vi.fn((body: unknown) => { res.body = body; return res; }); return res; } function makePgError(code: string, message: string): DatabaseError { // pg's DatabaseError ctor isn't ergonomic; build a shaped instance. const err = new DatabaseError(message, message.length, 'error'); Object.assign(err, { code }); return err; } describe('errorMiddleware', () => { it('translates Postgres 42501 (RLS WITH CHECK denial) into 403 with FORBIDDEN', () => { const err = makePgError( '42501', 'new row violates row-level security policy for table "objects"' ); const req = {} as Request; const res = makeMockRes(); const next = vi.fn(); errorMiddleware(err, req, res, next); expect(res.statusCode).toBe(403); const body = res.body as { error: string; message: string; nextActions?: string }; expect(body.error).toBe(ERROR_CODES.FORBIDDEN); expect(body.message).toContain('row-level security'); // The hint should mention RLS so a developer can self-diagnose. expect(body.nextActions).toMatch(/row-level security|RLS|policy/i); }); it('still maps 23505 (unique violation) to 409 — regression check', () => { const err = makePgError('23505', 'duplicate key value violates unique constraint'); err.detail = 'Key (key)=(note.txt) already exists.'; const req = {} as Request; const res = makeMockRes(); const next = vi.fn(); errorMiddleware(err, req, res, next); expect(res.statusCode).toBe(409); }); it('passes AppError through with its declared status', () => { const err = new AppError('bad input', 400, ERROR_CODES.INVALID_INPUT); const req = {} as Request; const res = makeMockRes(); const next = vi.fn(); errorMiddleware(err, req, res, next); expect(res.statusCode).toBe(400); const body = res.body as { error: string }; expect(body.error).toBe(ERROR_CODES.INVALID_INPUT); }); it('falls back to 500 for unknown errors even when they carry a 4xx status', () => { const req = {} as Request; const res = makeMockRes(); const next = vi.fn(); errorMiddleware({ status: 404, message: 'not found from an unknown source' }, req, res, next); expect(res.statusCode).toBe(500); const body = res.body as { error: string; message: string }; expect(body.error).toBe(ERROR_CODES.INTERNAL_ERROR); expect(body.message).toBe('not found from an unknown source'); }); it('falls back to 500 for unrecognized pg codes', () => { const err = makePgError('XX000', 'internal error'); const req = {} as Request; const res = makeMockRes(); const next = vi.fn(); errorMiddleware(err, req, res, next); expect(res.statusCode).toBe(500); }); });