Reformat code for readability
CI/CD Pipeline / test (push) Successful in 1m41s
CI/CD Pipeline / deploy-internal (push) Successful in 54s

This commit is contained in:
chris committed 2026-08-28 20:56:16 -04:00
1 parent 58a96f6d19
commit d505320c50
2 files changed
+88 -88

No files matched your search

+66 -66
View File
@@ -6,99 +6,99 @@ import { Request, Response } from 'express';
// 1. Generate a mock factory for the Express Response object // 1. Generate a mock factory for the Express Response object
function createMockResponse() { function createMockResponse() {
const res: Partial<Response> = {}; const res: Partial<Response> = {};
const data: { statusCode: number; jsonPayload: any } = { statusCode: 200, jsonPayload: null }; const data: { statusCode: number; jsonPayload: any } = { statusCode: 200, jsonPayload: null };
res.status = function (code: number) { res.status = function (code: number) {
data.statusCode = code; data.statusCode = code;
return this as Response; return this as Response;
}; };
res.json = function (payload: any) { res.json = function (payload: any) {
data.jsonPayload = payload; data.jsonPayload = payload;
return this as Response; return this as Response;
}; };
return { mockRes: res as Response, data }; return { mockRes: res as Response, data };
} }
// 2. Generate a mock factory for the repository layer // 2. Generate a mock factory for the repository layer
function createMockRepository(behavior: 'success' | 'failure') { function createMockRepository(behavior: 'success' | 'failure') {
return { return {
findAllDescendants: async (path: string) => { findAllDescendants: async (path: string) => {
if (behavior === 'failure') throw new Error('Database connection failed'); if (behavior === 'failure') throw new Error('Database connection failed');
// Return a valid single-root array format mimicking real ltree database outputs // Return a valid single-root array format mimicking real ltree database outputs
return [ return [
{ id: 1, name: 'Top', path: 'Top' }, { id: 1, name: 'Top', path: 'Top' },
{ id: 2, name: 'Science', path: 'Top.Science' } { id: 2, name: 'Science', path: 'Top.Science' }
]; ];
} }
} as unknown as CategoryRepository; } as unknown as CategoryRepository;
} }
// Helper utility to safely extract the Express handler bypassing strict undefined types // Helper utility to safely extract the Express handler bypassing strict undefined types
function getRouteHandler(router: any): Function { function getRouteHandler(router: any): Function {
const routeLayer = router.stack.find((layer: any) => layer.route); const routeLayer = router.stack.find((layer: any) => layer.route);
if (!routeLayer || !routeLayer.route || !routeLayer.route.stack) { if (!routeLayer || !routeLayer.route || !routeLayer.route.stack) {
throw new Error('Could not find route handler in mock router stack'); throw new Error('Could not find route handler in mock router stack');
} }
return routeLayer.route.stack[0].handle; // Target the primary callback handler array element return routeLayer.route.stack[0].handle; // Target the primary callback handler array element
} }
describe('Category Router Endpoints', () => { describe('Category Router Endpoints', () => {
it('GET /tree{/:path} - should return 200 and structural JSON array data on valid requests', async () => { it('GET /tree{/:path} - should return 200 and structural JSON array data on valid requests', async () => {
const mockRepo = createMockRepository('success'); const mockRepo = createMockRepository('success');
const router = createCategoryRouter(mockRepo); const router = createCategoryRouter(mockRepo);
const mockReq = { params: { path: 'Top' } } as unknown as Request; const mockReq = { params: { path: 'Top' } } as unknown as Request;
const { mockRes, data } = createMockResponse(); const { mockRes, data } = createMockResponse();
const routeHandler = getRouteHandler(router); const routeHandler = getRouteHandler(router);
await routeHandler(mockReq, mockRes); await routeHandler(mockReq, mockRes);
assert.equal(data.statusCode, 200); assert.equal(data.statusCode, 200);
assert.equal(data.jsonPayload.success, true); assert.equal(data.jsonPayload.success, true);
// Correct alignment: buildCategoryTree returns an ARRAY of root nodes // Correct alignment: buildCategoryTree returns an ARRAY of root nodes
assert.equal(Array.isArray(data.jsonPayload.data), true); assert.equal(Array.isArray(data.jsonPayload.data), true);
assert.equal(data.jsonPayload.data[0].name, 'Top'); assert.equal(data.jsonPayload.data[0].name, 'Top');
}); });
it('GET /tree{/:path} - should return 400 Bad Request if ltree path contains malformed formatting', async () => { it('GET /tree{/:path} - should return 400 Bad Request if ltree path contains malformed formatting', async () => {
const mockRepo = createMockRepository('success'); const mockRepo = createMockRepository('success');
const router = createCategoryRouter(mockRepo); const router = createCategoryRouter(mockRepo);
const mockReq = { params: { path: 'Top.Bad Path!' } } as unknown as Request; const mockReq = { params: { path: 'Top.Bad Path!' } } as unknown as Request;
const { mockRes, data } = createMockResponse(); const { mockRes, data } = createMockResponse();
const routeHandler = getRouteHandler(router); const routeHandler = getRouteHandler(router);
await routeHandler(mockReq, mockRes); await routeHandler(mockReq, mockRes);
assert.equal(data.statusCode, 400); assert.equal(data.statusCode, 400);
assert.equal(data.jsonPayload.success, false); assert.equal(data.jsonPayload.success, false);
assert.match(data.jsonPayload.error, /Invalid path format/); assert.match(data.jsonPayload.error, /Invalid path format/);
}); });
it('GET /tree{/:path} - should handle runtime database crashes gracefully with a 500 error status', async () => { it('GET /tree{/:path} - should handle runtime database crashes gracefully with a 500 error status', async () => {
const mockRepo = createMockRepository('failure'); const mockRepo = createMockRepository('failure');
const router = createCategoryRouter(mockRepo); const router = createCategoryRouter(mockRepo);
const mockReq = { params: { path: 'Top' } } as unknown as Request; const mockReq = { params: { path: 'Top' } } as unknown as Request;
const { mockRes, data } = createMockResponse(); const { mockRes, data } = createMockResponse();
// Prevent console.error from cluttering the test runner feedback during intentional failures // Prevent console.error from cluttering the test runner feedback during intentional failures
const originalConsoleError = console.error; const originalConsoleError = console.error;
console.error = () => {}; console.error = () => { };
const routeHandler = getRouteHandler(router); const routeHandler = getRouteHandler(router);
await routeHandler(mockReq, mockRes); await routeHandler(mockReq, mockRes);
// Restore original logger tracking // Restore original logger tracking
console.error = originalConsoleError; console.error = originalConsoleError;
assert.equal(data.statusCode, 500); assert.equal(data.statusCode, 500);
assert.equal(data.jsonPayload.success, false); assert.equal(data.jsonPayload.success, false);
assert.equal(data.jsonPayload.error, 'Internal Server Error'); assert.equal(data.jsonPayload.error, 'Internal Server Error');
}); });
}); });
+22 -22
View File
@@ -6,31 +6,31 @@ import { buildCategoryTree } from './tree.utility.ts';
const LTREE_REGEX = /^[A-Za-z0-9_]+(\.[A-Za-z0-9_]+)*$/; const LTREE_REGEX = /^[A-Za-z0-9_]+(\.[A-Za-z0-9_]+)*$/;
export function createCategoryRouter(repository: CategoryRepository): Router { export function createCategoryRouter(repository: CategoryRepository): Router {
const router = Router(); const router = Router();
router.get('/tree{/:path}', async (req: Request, res: Response): Promise<void> => { router.get('/tree{/:path}', async (req: Request, res: Response): Promise<void> => {
try { try {
const rawPath = req.params.path; const rawPath = req.params.path;
const parentPath = Array.isArray(rawPath) ? rawPath[0] : (rawPath || 'Top'); const parentPath = Array.isArray(rawPath) ? rawPath[0] : (rawPath || 'Top');
// 1. INPUT VALIDATION: Stop malicious or broken ltree strings early // 1. INPUT VALIDATION: Stop malicious or broken ltree strings early
if (!LTREE_REGEX.test(parentPath)) { if (!LTREE_REGEX.test(parentPath)) {
res.status(400).json({ res.status(400).json({
success: false, success: false,
error: 'Invalid path format. Path elements must be alphanumeric separated by dots.' error: 'Invalid path format. Path elements must be alphanumeric separated by dots.'
}); });
return; // Break execution early return; // Break execution early
} }
const flatRows = await repository.findAllDescendants(parentPath); const flatRows = await repository.findAllDescendants(parentPath);
const nestedTree = buildCategoryTree(flatRows); const nestedTree = buildCategoryTree(flatRows);
res.json({ success: true, data: nestedTree }); res.json({ success: true, data: nestedTree });
} catch (error) { } catch (error) {
console.error('Failed to resolve category tree:', error); console.error('Failed to resolve category tree:', error);
res.status(500).json({ success: false, error: 'Internal Server Error' }); res.status(500).json({ success: false, error: 'Internal Server Error' });
} }
}); });
return router; return router;
} }