Files
flyer-crawler.projectium.com/src/services/db/errors.db.test.ts
Torben Sorensen e86e09703e
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 59s
even even more and more test fixes
2026-01-05 11:27:13 -08:00

303 lines
11 KiB
TypeScript

// src/services/db/errors.db.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import type { Logger } from 'pino';
import {
RepositoryError,
UniqueConstraintError,
ForeignKeyConstraintError,
NotFoundError,
ForbiddenError,
ValidationError,
FileUploadError,
NotNullConstraintError,
CheckConstraintError,
InvalidTextRepresentationError,
NumericValueOutOfRangeError,
handleDbError,
} from './errors.db';
vi.mock('./logger.server');
describe('Custom Database and Application Errors', () => {
describe('RepositoryError', () => {
it('should create a generic database error with a message and status', () => {
const message = 'Generic DB Error';
const status = 500;
const error = new RepositoryError(message, status);
expect(error).toBeInstanceOf(Error);
expect(error).toBeInstanceOf(RepositoryError);
expect(error.message).toBe(message);
expect(error.status).toBe(status);
expect(error.name).toBe('RepositoryError');
});
});
describe('UniqueConstraintError', () => {
it('should create an error with a default message and status 409', () => {
const error = new UniqueConstraintError();
expect(error).toBeInstanceOf(Error);
expect(error).toBeInstanceOf(RepositoryError);
expect(error).toBeInstanceOf(UniqueConstraintError);
expect(error.message).toBe('The record already exists.');
expect(error.status).toBe(409);
expect(error.name).toBe('UniqueConstraintError');
});
it('should create an error with a custom message', () => {
const message = 'This email is already taken.';
const error = new UniqueConstraintError(message);
expect(error.message).toBe(message);
});
});
describe('ForeignKeyConstraintError', () => {
it('should create an error with a default message and status 400', () => {
const error = new ForeignKeyConstraintError();
expect(error).toBeInstanceOf(Error);
expect(error).toBeInstanceOf(RepositoryError);
expect(error).toBeInstanceOf(ForeignKeyConstraintError);
expect(error.message).toBe('The referenced record does not exist.');
expect(error.status).toBe(400);
expect(error.name).toBe('ForeignKeyConstraintError');
});
it('should create an error with a custom message', () => {
const message = 'The specified user does not exist.';
const error = new ForeignKeyConstraintError(message);
expect(error.message).toBe(message);
});
});
describe('NotFoundError', () => {
it('should create an error with a default message and status 404', () => {
const error = new NotFoundError();
expect(error).toBeInstanceOf(Error);
expect(error).toBeInstanceOf(RepositoryError);
expect(error).toBeInstanceOf(NotFoundError);
expect(error.message).toBe('The requested resource was not found.');
expect(error.status).toBe(404);
expect(error.name).toBe('NotFoundError');
});
it('should create an error with a custom message', () => {
const message = 'Flyer with ID 999 not found.';
const error = new NotFoundError(message);
expect(error.message).toBe(message);
});
});
describe('ForbiddenError', () => {
it('should create an error with a default message and status 403', () => {
const error = new ForbiddenError();
expect(error).toBeInstanceOf(Error);
expect(error).toBeInstanceOf(RepositoryError);
expect(error).toBeInstanceOf(ForbiddenError);
expect(error.message).toBe('Access denied.');
expect(error.status).toBe(403);
expect(error.name).toBe('ForbiddenError');
});
it('should create an error with a custom message', () => {
const message = 'You shall not pass.';
const error = new ForbiddenError(message);
expect(error.message).toBe(message);
});
});
describe('ValidationError', () => {
it('should create an error with a default message, status 400, and validation errors array', () => {
const validationIssues = [{ path: ['email'], message: 'Invalid email' }];
const error = new ValidationError(validationIssues);
expect(error).toBeInstanceOf(Error);
expect(error).toBeInstanceOf(RepositoryError);
expect(error).toBeInstanceOf(ValidationError);
expect(error.message).toBe('The request data is invalid.');
expect(error.status).toBe(400);
expect(error.name).toBe('ValidationError');
expect(error.validationErrors).toEqual(validationIssues);
});
it('should create an error with a custom message', () => {
const message = 'Your input has some issues.';
const error = new ValidationError([], message);
expect(error.message).toBe(message);
});
});
describe('FileUploadError', () => {
it('should create an error with the correct message, name, and status 400', () => {
const message = 'No file was uploaded.';
const error = new FileUploadError(message);
expect(error).toBeInstanceOf(Error);
expect(error).toBeInstanceOf(FileUploadError);
expect(error.message).toBe(message);
expect(error.status).toBe(400);
expect(error.name).toBe('FileUploadError');
});
});
describe('NotNullConstraintError', () => {
it('should create an error with a default message and status 400', () => {
const error = new NotNullConstraintError();
expect(error).toBeInstanceOf(RepositoryError);
expect(error.message).toBe('A required field was left null.');
expect(error.status).toBe(400);
expect(error.name).toBe('NotNullConstraintError');
});
it('should create an error with a custom message', () => {
const message = 'Email cannot be null.';
const error = new NotNullConstraintError(message);
expect(error.message).toBe(message);
});
});
describe('CheckConstraintError', () => {
it('should create an error with a default message and status 400', () => {
const error = new CheckConstraintError();
expect(error).toBeInstanceOf(RepositoryError);
expect(error.message).toBe('A check constraint was violated.');
expect(error.status).toBe(400);
expect(error.name).toBe('CheckConstraintError');
});
it('should create an error with a custom message', () => {
const message = 'Price must be positive.';
const error = new CheckConstraintError(message);
expect(error.message).toBe(message);
});
});
describe('InvalidTextRepresentationError', () => {
it('should create an error with a default message and status 400', () => {
const error = new InvalidTextRepresentationError();
expect(error).toBeInstanceOf(RepositoryError);
expect(error.message).toBe('A value has an invalid format for its data type.');
expect(error.status).toBe(400);
expect(error.name).toBe('InvalidTextRepresentationError');
});
it('should create an error with a custom message', () => {
const message = 'Invalid input syntax for type integer: "abc"';
const error = new InvalidTextRepresentationError(message);
expect(error.message).toBe(message);
});
});
describe('NumericValueOutOfRangeError', () => {
it('should create an error with a default message and status 400', () => {
const error = new NumericValueOutOfRangeError();
expect(error).toBeInstanceOf(RepositoryError);
expect(error.message).toBe('A numeric value is out of the allowed range.');
expect(error.status).toBe(400);
expect(error.name).toBe('NumericValueOutOfRangeError');
});
it('should create an error with a custom message', () => {
const message = 'Value too large for type smallint.';
const error = new NumericValueOutOfRangeError(message);
expect(error.message).toBe(message);
});
});
describe('handleDbError', () => {
const mockLogger = {
error: vi.fn(),
} as unknown as Logger;
beforeEach(() => {
vi.clearAllMocks();
});
it('should re-throw existing RepositoryError instances without logging', () => {
const notFound = new NotFoundError('Test not found');
expect(() => handleDbError(notFound, mockLogger, 'msg', {})).toThrow(notFound);
expect(mockLogger.error).not.toHaveBeenCalled();
});
it('should throw UniqueConstraintError for code 23505', () => {
const dbError = new Error('duplicate key');
(dbError as any).code = '23505';
expect(() =>
handleDbError(dbError, mockLogger, 'msg', {}, { uniqueMessage: 'custom unique' }),
).toThrow('custom unique');
});
it('should throw ForeignKeyConstraintError for code 23503', () => {
const dbError = new Error('fk violation');
(dbError as any).code = '23503';
expect(() =>
handleDbError(dbError, mockLogger, 'msg', {}, { fkMessage: 'custom fk' }),
).toThrow('custom fk');
});
it('should throw NotNullConstraintError for code 23502', () => {
const dbError = new Error('not null violation');
(dbError as any).code = '23502';
expect(() =>
handleDbError(dbError, mockLogger, 'msg', {}, { notNullMessage: 'custom not null' }),
).toThrow('custom not null');
});
it('should throw CheckConstraintError for code 23514', () => {
const dbError = new Error('check violation');
(dbError as any).code = '23514';
expect(() =>
handleDbError(dbError, mockLogger, 'msg', {}, { checkMessage: 'custom check' }),
).toThrow('custom check');
});
it('should throw InvalidTextRepresentationError for code 22P02', () => {
const dbError = new Error('invalid text');
(dbError as any).code = '22P02';
expect(() =>
handleDbError(dbError, mockLogger, 'msg', {}, { invalidTextMessage: 'custom invalid text' }),
).toThrow('custom invalid text');
});
it('should throw NumericValueOutOfRangeError for code 22003', () => {
const dbError = new Error('out of range');
(dbError as any).code = '22003';
expect(() =>
handleDbError(
dbError,
mockLogger,
'msg',
{},
{ numericOutOfRangeMessage: 'custom out of range' },
),
).toThrow('custom out of range');
});
it('should throw a generic Error with a default message', () => {
const genericError = new Error('Something else happened');
expect(() =>
handleDbError(genericError, mockLogger, 'msg', {}, { defaultMessage: 'Oops' }),
).toThrow('Oops');
expect(mockLogger.error).toHaveBeenCalledWith({ err: genericError }, 'msg');
});
it('should throw a generic Error with a constructed message using entityName', () => {
const genericError = new Error('Something else happened');
expect(() =>
handleDbError(genericError, mockLogger, 'msg', {}, { entityName: 'User' }),
).toThrow('Failed to perform operation on User.');
});
it('should throw a generic Error with a constructed message using "database" as a fallback', () => {
const genericError = new Error('Something else happened');
// No defaultMessage or entityName provided
expect(() => handleDbError(genericError, mockLogger, 'msg', {}, {})).toThrow(
'Failed to perform operation on database.',
);
});
});
});