Files
dashcaddy/dashcaddy-api/__tests__/helpers/test-utils.js
Sami ea5acfa9a2 test: build comprehensive test suite reaching 80%+ coverage threshold
Add 22 test files (~700 tests) covering security-critical modules, core
infrastructure, API routes, and error handling. Final coverage: 86.73%
statements / 80.57% branches / 85.57% functions / 87.42% lines, all above
the 80% threshold enforced by jest.config.js.

Highlights:
- Unit tests for crypto-utils, credential-manager, auth-manager, csrf,
  input-validator, state-manager, health-checker, backup-manager,
  update-manager, resource-monitor, app-templates, platform-paths,
  port-lock-manager, errors, error-handler, pagination, url-resolver
- Route tests for health, services, and containers (supertest + mocked deps)
- Shared test-utils helper for mock factories and Express app builder
- npm scripts for CI: test:ci, test:unit, test:routes, test:security,
  test:changed, test:debug
- jest.config.js: expand coverage targets, add 80% threshold gate
- routes/services.js: import ValidationError and NotFoundError from errors
- .gitignore: exclude coverage/, *.bak, *.log

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 21:36:46 -07:00

141 lines
3.7 KiB
JavaScript

/**
* Shared test utilities for DashCaddy test suite
*/
const express = require('express');
/**
* Create a mock credential manager
*/
function createMockCredentialManager() {
return {
store: jest.fn().mockResolvedValue(true),
retrieve: jest.fn().mockResolvedValue(null),
delete: jest.fn().mockResolvedValue(true),
list: jest.fn().mockResolvedValue([]),
getMetadata: jest.fn().mockResolvedValue(null),
rotateEncryptionKey: jest.fn().mockResolvedValue(true),
exportBackup: jest.fn().mockResolvedValue('encrypted-backup'),
importBackup: jest.fn().mockResolvedValue(true),
};
}
/**
* Create a mock crypto utils module
*/
function createMockCryptoUtils() {
const fixedKey = Buffer.alloc(32, 'a');
return {
encrypt: jest.fn(data => `mock-iv:mock-tag:${Buffer.from(String(data)).toString('base64')}`),
decrypt: jest.fn(data => {
const parts = data.split(':');
return Buffer.from(parts[2], 'base64').toString('utf8');
}),
isEncrypted: jest.fn(data => typeof data === 'string' && data.split(':').length === 3),
encryptFields: jest.fn((obj, fields) => ({ ...obj, _encrypted: true, _encryptedFields: fields })),
decryptFields: jest.fn(obj => {
const result = { ...obj };
delete result._encrypted;
delete result._encryptedFields;
return result;
}),
loadOrCreateKey: jest.fn(() => fixedKey),
clearCachedKey: jest.fn(),
rotateKey: jest.fn(() => ({ oldKey: fixedKey, newKey: Buffer.alloc(32, 'b') })),
deriveKey: jest.fn().mockResolvedValue(fixedKey),
decryptWithKey: jest.fn(data => {
const parts = data.split(':');
return Buffer.from(parts[2], 'base64').toString('utf8');
}),
readEncryptedFile: jest.fn().mockReturnValue(null),
writeEncryptedFile: jest.fn(),
migrateToEncrypted: jest.fn(obj => obj),
};
}
/**
* Create a mock state manager
*/
function createMockStateManager() {
let data = [];
return {
read: jest.fn().mockResolvedValue(data),
write: jest.fn().mockResolvedValue(),
update: jest.fn(async fn => { data = fn(data); return data; }),
addItem: jest.fn().mockResolvedValue(),
removeItem: jest.fn().mockResolvedValue(),
updateItem: jest.fn().mockResolvedValue(),
findItem: jest.fn().mockResolvedValue(null),
_setData: (newData) => { data = newData; },
};
}
/**
* Create a mock logger
*/
function createMockLogger() {
return {
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
debug: jest.fn(),
};
}
/**
* Build a minimal Express app for route testing with supertest
*/
function buildTestApp(routeFactory, deps, prefix = '/api') {
const app = express();
app.use(express.json());
const router = routeFactory(deps);
app.use(prefix, router);
// Error handler
const { errorMiddleware } = require('../../error-handler');
app.use(errorMiddleware);
return app;
}
/**
* Create mock Express req/res/next for middleware testing
*/
function createMockReqRes(overrides = {}) {
const req = {
method: 'GET',
path: '/test',
headers: {},
cookies: {},
ip: '127.0.0.1',
protocol: 'https',
secure: true,
body: {},
params: {},
query: {},
get: jest.fn(header => req.headers[header.toLowerCase()]),
...overrides,
};
const res = {
status: jest.fn().mockReturnThis(),
json: jest.fn().mockReturnThis(),
send: jest.fn().mockReturnThis(),
set: jest.fn().mockReturnThis(),
cookie: jest.fn().mockReturnThis(),
setHeader: jest.fn().mockReturnThis(),
getHeader: jest.fn(),
end: jest.fn(),
};
const next = jest.fn();
return { req, res, next };
}
module.exports = {
createMockCredentialManager,
createMockCryptoUtils,
createMockStateManager,
createMockLogger,
buildTestApp,
createMockReqRes,
};