Files
Hermes 7bc2a207f3 DC-005: Fix all 138 broken test paths after src/ refactor
After the DC-005 module reorganization (41 files moved into src/ subdirs),
138 test suites failed because the refactor script's path-rewrite logic
missed three categories:

1. Files inside src/ doing 'require("./src/...")' — should be 'require("../...")'
2. Files in src/X/Y/ doing 'require("../../../src/...")' — should be 'require("../../...")'
3. Test files in __tests__/ with leftover 'require("../../../src/...")' paths

Root cause: the original refactor script ran before all files were moved,
so it computed relative paths against stale filesystem state.

Result:
- 30/30 test suites pass
- 879/879 tests pass (was: 18/30 suites, 614/687 tests)

Also fixed:
- routes/apps/restore.js: wrong responses import path
- routes/*/*.js: '../../src/utilities/X' → '../src/utilities/X' (depth 2 routes)
2026-06-13 12:16:56 -07:00

158 lines
4.9 KiB
JavaScript

const {
AppError,
ValidationError,
AuthenticationError,
ForbiddenError,
NotFoundError,
ConflictError,
RateLimitError,
DockerError,
CaddyError,
DNSError,
ServiceUnavailableError
} = require('../src/utilities/errors');
describe('Error Classes', () => {
describe('AppError', () => {
it('has default statusCode 500 and auto-generated code', () => {
const err = new AppError('something broke');
expect(err.message).toBe('something broke');
expect(err.statusCode).toBe(500);
expect(err.code).toBe('APP_ERROR');
expect(err.isOperational).toBe(true);
expect(err).toBeInstanceOf(Error);
});
it('accepts custom statusCode and code', () => {
const err = new AppError('custom', 418, 'DC-TEAPOT');
expect(err.statusCode).toBe(418);
expect(err.code).toBe('DC-TEAPOT');
});
});
describe('ValidationError', () => {
it('has statusCode 400, code DC-400, and optional field', () => {
const err = new ValidationError('bad input', 'email');
expect(err.statusCode).toBe(400);
expect(err.code).toBe('DC-400');
expect(err.field).toBe('email');
expect(err).toBeInstanceOf(AppError);
});
it('field defaults to null', () => {
const err = new ValidationError('bad');
expect(err.field).toBeNull();
});
});
describe('AuthenticationError', () => {
it('has statusCode 401 and requiresTotp flag', () => {
const err = new AuthenticationError('need auth', true);
expect(err.statusCode).toBe(401);
expect(err.code).toBe('DC-401');
expect(err.requiresTotp).toBe(true);
expect(err).toBeInstanceOf(AppError);
});
it('has sensible defaults', () => {
const err = new AuthenticationError();
expect(err.message).toBe('Authentication required');
expect(err.requiresTotp).toBe(false);
});
});
describe('ForbiddenError', () => {
it('has statusCode 403', () => {
const err = new ForbiddenError();
expect(err.statusCode).toBe(403);
expect(err.code).toBe('DC-403');
expect(err.message).toBe('Forbidden');
expect(err).toBeInstanceOf(AppError);
});
});
describe('NotFoundError', () => {
it('has statusCode 404 and resource in message', () => {
const err = new NotFoundError('Service');
expect(err.statusCode).toBe(404);
expect(err.code).toBe('DC-404');
expect(err.message).toBe('Service not found');
expect(err.resource).toBe('Service');
expect(err).toBeInstanceOf(AppError);
});
it('defaults to "Resource"', () => {
const err = new NotFoundError();
expect(err.message).toBe('Resource not found');
});
});
describe('ConflictError', () => {
it('has statusCode 409 and optional conflictingResource', () => {
const err = new ConflictError('already exists', 'service-x');
expect(err.statusCode).toBe(409);
expect(err.code).toBe('DC-409');
expect(err.conflictingResource).toBe('service-x');
expect(err).toBeInstanceOf(AppError);
});
});
describe('RateLimitError', () => {
it('has statusCode 429 and retryAfter', () => {
const err = new RateLimitError(30);
expect(err.statusCode).toBe(429);
expect(err.code).toBe('DC-429');
expect(err.retryAfter).toBe(30);
expect(err.message).toBe('Rate limit exceeded');
expect(err).toBeInstanceOf(AppError);
});
it('defaults retryAfter to 60', () => {
const err = new RateLimitError();
expect(err.retryAfter).toBe(60);
});
});
describe('DockerError', () => {
it('has statusCode 500, operation, and details', () => {
const err = new DockerError('container failed', 'create', { containerId: '123' });
expect(err.statusCode).toBe(500);
expect(err.code).toBe('DC-500-DOCKER');
expect(err.operation).toBe('create');
expect(err.details).toEqual({ containerId: '123' });
expect(err).toBeInstanceOf(AppError);
});
});
describe('CaddyError', () => {
it('has statusCode 502', () => {
const err = new CaddyError('reload failed', 'reload');
expect(err.statusCode).toBe(502);
expect(err.code).toBe('DC-502-CADDY');
expect(err.operation).toBe('reload');
expect(err).toBeInstanceOf(AppError);
});
});
describe('DNSError', () => {
it('has statusCode 502', () => {
const err = new DNSError('zone create failed', 'create-zone');
expect(err.statusCode).toBe(502);
expect(err.code).toBe('DC-502-DNS');
expect(err).toBeInstanceOf(AppError);
});
});
describe('ServiceUnavailableError', () => {
it('has statusCode 503, service name, and optional retryAfter', () => {
const err = new ServiceUnavailableError('plex', 120);
expect(err.statusCode).toBe(503);
expect(err.code).toBe('DC-503');
expect(err.message).toBe('Service unavailable: plex');
expect(err.service).toBe('plex');
expect(err.retryAfter).toBe(120);
expect(err).toBeInstanceOf(AppError);
});
});
});