Update router and tests to fix issues

This commit is contained in:
chris committed 2026-08-28 20:36:24 -04:00
1 parent 223ab79d21
commit af4f5d87ca
2 files changed
+120 -50

No files matched your search

+94 -25
View File
@@ -1,35 +1,104 @@
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { buildCategoryTree, CategoryNode } from './tree.utility.ts';
import { createCategoryRouter } from './category.router.ts';
import { CategoryRepository } from './category.repository.ts';
import { Request, Response } from 'express';
describe('Tree Utility - buildCategoryTree()', () => {
it('should transform a flat list of ltree rows into a nested JSON structure', () => {
const flatNodes: CategoryNode[] = [
{ id: 1, name: 'Root', path: 'Top' },
{ id: 3, name: 'Astronomy', path: 'Top.Science.Astronomy' },
{ id: 2, name: 'Science', path: 'Top.Science' },
{ id: 4, name: 'Gardening', path: 'Top.Hobbies.Gardening' },
{ id: 5, name: 'Hobbies', path: 'Top.Hobbies' }
];
// 1. Generate a mock factory for the Express Response object
function createMockResponse() {
const res: Partial<Response> = {};
const data: { statusCode: number; jsonPayload: any } = { statusCode: 200, jsonPayload: null };
const tree = buildCategoryTree(flatNodes);
res.status = function (code: number) {
data.statusCode = code;
return this as Response;
};
// Verify root nodes count
assert.equal(tree.length, 1);
assert.equal(tree[0].name, 'Root');
res.json = function (payload: any) {
data.jsonPayload = payload;
return this as Response;
};
// Verify second level ("Science" and "Hobbies")
const rootChildren = tree[0].children;
assert.equal(rootChildren.length, 2);
return { mockRes: res as Response, data };
}
const scienceNode = rootChildren.find(c => c.name === 'Science');
const hobbiesNode = rootChildren.find(c => c.name === 'Hobbies');
// 2. Generate a mock factory for the repository layer
function createMockRepository(behavior: 'success' | 'failure') {
return {
findAllDescendants: async (path: string) => {
if (behavior === 'failure') throw new Error('Database connection failed');
// Return a valid single-root array format mimicking real ltree database outputs
return [
{ id: 1, name: 'Top', path: 'Top' },
{ id: 2, name: 'Science', path: 'Top.Science' }
];
}
} as unknown as CategoryRepository;
}
assert.ok(scienceNode);
assert.ok(hobbiesNode);
// Helper utility to safely extract the Express handler bypassing strict undefined types
function getRouteHandler(router: any): Function {
const routeLayer = router.stack.find((layer: any) => layer.route);
if (!routeLayer || !routeLayer.route || !routeLayer.route.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
}
// Verify third level deeply nested child ("Astronomy")
assert.equal(scienceNode.children.length, 1);
assert.equal(scienceNode.children[0].name, 'Astronomy');
});
describe('Category Router Endpoints', () => {
it('GET /tree{/:path} - should return 200 and structural JSON array data on valid requests', async () => {
const mockRepo = createMockRepository('success');
const router = createCategoryRouter(mockRepo);
const mockReq = { params: { path: 'Top' } } as unknown as Request;
const { mockRes, data } = createMockResponse();
const routeHandler = getRouteHandler(router);
await routeHandler(mockReq, mockRes);
assert.equal(data.statusCode, 200);
assert.equal(data.jsonPayload.success, true);
// Correct alignment: buildCategoryTree returns an ARRAY of root nodes
assert.equal(Array.isArray(data.jsonPayload.data), true);
assert.equal(data.jsonPayload.data[0].name, 'Top');
});
it('GET /tree{/:path} - should return 400 Bad Request if ltree path contains malformed formatting', async () => {
const mockRepo = createMockRepository('success');
const router = createCategoryRouter(mockRepo);
const mockReq = { params: { path: 'Top.Bad Path!' } } as unknown as Request;
const { mockRes, data } = createMockResponse();
const routeHandler = getRouteHandler(router);
await routeHandler(mockReq, mockRes);
assert.equal(data.statusCode, 400);
assert.equal(data.jsonPayload.success, false);
assert.match(data.jsonPayload.error, /Invalid path format/);
});
it('GET /tree{/:path} - should handle runtime database crashes gracefully with a 500 error status', async () => {
const mockRepo = createMockRepository('failure');
const router = createCategoryRouter(mockRepo);
const mockReq = { params: { path: 'Top' } } as unknown as Request;
const { mockRes, data } = createMockResponse();
// Prevent console.error from cluttering the test runner feedback during intentional failures
const originalConsoleError = console.error;
console.error = () => {};
const routeHandler = getRouteHandler(router);
await routeHandler(mockReq, mockRes);
// Restore original logger tracking
console.error = originalConsoleError;
assert.equal(data.statusCode, 500);
assert.equal(data.jsonPayload.success, false);
assert.equal(data.jsonPayload.error, 'Internal Server Error');
});
});