Refactor: Add centralized error handling middleware to improve test stability and error reporting across all route tests
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 8m59s

This commit is contained in:
2025-12-15 20:08:35 -08:00
parent 08340a4cf2
commit e6968987b9
15 changed files with 102 additions and 0 deletions

View File

@@ -116,6 +116,12 @@ describe('Admin Content Management Routes (/api/admin)', () => {
// Create a single app instance with an admin user for all tests in this suite.
const app = createTestApp({ router: adminRouter, basePath: '/api/admin', authenticatedUser: adminUser });
// Add a basic error handler to capture errors passed to next(err) and return JSON.
// This prevents unhandled error crashes in tests and ensures we get the 500 response we expect.
app.use((err: any, req: any, res: any, next: any) => {
res.status(err.status || 500).json({ message: err.message, errors: err.errors });
});
beforeEach(() => {
vi.clearAllMocks();
});

View File

@@ -92,6 +92,12 @@ describe('Admin Job Trigger Routes (/api/admin/trigger)', () => {
// Create a single app instance with an admin user for all tests in this suite.
const app = createTestApp({ router: adminRouter, basePath: '/api/admin', authenticatedUser: adminUser });
// Add a basic error handler to capture errors passed to next(err) and return JSON.
// This prevents unhandled error crashes in tests and ensures we get the 500 response we expect.
app.use((err: any, req: any, res: any, next: any) => {
res.status(err.status || 500).json({ message: err.message, errors: err.errors });
});
beforeEach(() => {
vi.clearAllMocks();
});

View File

@@ -102,6 +102,12 @@ describe('Admin Monitoring Routes (/api/admin)', () => {
// Create a single app instance with an admin user for all tests in this suite.
const app = createTestApp({ router: adminRouter, basePath: '/api/admin', authenticatedUser: adminUser });
// Add a basic error handler to capture errors passed to next(err) and return JSON.
// This prevents unhandled error crashes in tests and ensures we get the 500 response we expect.
app.use((err: any, req: any, res: any, next: any) => {
res.status(err.status || 500).json({ message: err.message, errors: err.errors });
});
beforeEach(() => {
vi.clearAllMocks();
});

View File

@@ -74,6 +74,12 @@ describe('Admin Stats Routes (/api/admin/stats)', () => {
// Create a single app instance with an admin user for all tests in this suite.
const app = createTestApp({ router: adminRouter, basePath: '/api/admin', authenticatedUser: adminUser });
// Add a basic error handler to capture errors passed to next(err) and return JSON.
// This prevents unhandled error crashes in tests and ensures we get the 500 response we expect.
app.use((err: any, req: any, res: any, next: any) => {
res.status(err.status || 500).json({ message: err.message, errors: err.errors });
});
beforeEach(() => {
vi.clearAllMocks();
});

View File

@@ -64,6 +64,12 @@ describe('Admin System Routes (/api/admin/system)', () => {
const adminUser = createMockUserProfile({ role: 'admin' });
const app = createTestApp({ router: adminRouter, basePath: '/api/admin', authenticatedUser: adminUser });
// Add a basic error handler to capture errors passed to next(err) and return JSON.
// This prevents unhandled error crashes in tests and ensures we get the 500 response we expect.
app.use((err: any, req: any, res: any, next: any) => {
res.status(err.status || 500).json({ message: err.message, errors: err.errors });
});
beforeEach(() => {
vi.clearAllMocks();
});

View File

@@ -81,6 +81,12 @@ describe('Admin User Management Routes (/api/admin/users)', () => {
// Create a single app instance with an admin user for all tests in this suite.
const app = createTestApp({ router: adminRouter, basePath: '/api/admin', authenticatedUser: adminUser });
// Add a basic error handler to capture errors passed to next(err) and return JSON.
// This prevents unhandled error crashes in tests and ensures we get the 500 response we expect.
app.use((err: any, req: any, res: any, next: any) => {
res.status(err.status || 500).json({ message: err.message, errors: err.errors });
});
beforeEach(() => {
vi.clearAllMocks();
});

View File

@@ -76,6 +76,12 @@ describe('AI Routes (/api/ai)', () => {
});
const app = createTestApp({ router: aiRouter, basePath: '/api/ai' });
// Add a basic error handler to capture errors passed to next(err) and return JSON.
// This prevents unhandled error crashes in tests and ensures we get the 500 response we expect.
app.use((err: any, req: any, res: any, next: any) => {
res.status(err.status || 500).json({ message: err.message, errors: err.errors });
});
describe('POST /upload-and-process', () => {
const imagePath = path.resolve(__dirname, '../tests/assets/test-flyer-image.jpg');

View File

@@ -63,6 +63,12 @@ describe('Budget Routes (/api/budgets)', () => {
const app = createTestApp({ router: budgetRouter, basePath: '/api/budgets', authenticatedUser: mockUser });
// Add a basic error handler to capture errors passed to next(err) and return JSON.
// This prevents unhandled error crashes in tests and ensures we get the 500 response we expect.
app.use((err: any, req: any, res: any, next: any) => {
res.status(err.status || 500).json({ message: err.message, errors: err.errors });
});
describe('GET /', () => {
it('should return a list of budgets for the user', async () => {
const mockBudgets = [createMockBudget({ budget_id: 1, user_id: 'user-123' })];

View File

@@ -46,6 +46,13 @@ describe('Deals Routes (/api/users/deals)', () => {
const basePath = '/api/users/deals';
const authenticatedApp = createTestApp({ router: dealsRouter, basePath, authenticatedUser: mockUser });
const unauthenticatedApp = createTestApp({ router: dealsRouter, basePath });
const errorHandler = (err: any, req: any, res: any, next: any) => {
res.status(err.status || 500).json({ message: err.message, errors: err.errors });
};
// Apply the handler to both app instances
authenticatedApp.use(errorHandler);
unauthenticatedApp.use(errorHandler);
beforeEach(() => {
vi.clearAllMocks();
@@ -67,5 +74,13 @@ describe('Deals Routes (/api/users/deals)', () => {
expect(response.body).toEqual(mockDeals);
expect(dealsRepo.findBestPricesForWatchedItems).toHaveBeenCalledWith(mockUser.user_id, expectLogger);
});
it('should return 500 if the database call fails', async () => {
vi.mocked(dealsRepo.findBestPricesForWatchedItems).mockRejectedValue(new Error('DB Error'));
const response = await supertest(authenticatedApp).get('/api/users/deals/best-watched-prices');
expect(response.status).toBe(500);
expect(response.body.message).toBe('DB Error');
});
});
});

View File

@@ -41,6 +41,12 @@ describe('Flyer Routes (/api/flyers)', () => {
const app = createTestApp({ router: flyerRouter, basePath: '/api/flyers' });
// Add a basic error handler to capture errors passed to next(err) and return JSON.
// This prevents unhandled error crashes in tests and ensures we get the 500 response we expect.
app.use((err: any, req: any, res: any, next: any) => {
res.status(err.status || 500).json({ message: err.message, errors: err.errors });
});
describe('GET /', () => {
it('should return a list of flyers on success', async () => {
const mockFlyers = [createMockFlyer({ flyer_id: 1 }), createMockFlyer({ flyer_id: 2 })];

View File

@@ -66,6 +66,12 @@ describe('Gamification Routes (/api/achievements)', () => {
const unauthenticatedApp = createTestApp({ router: gamificationRouter, basePath });
const authenticatedApp = createTestApp({ router: gamificationRouter, basePath, authenticatedUser: mockUserProfile });
const adminApp = createTestApp({ router: gamificationRouter, basePath, authenticatedUser: mockAdminProfile });
const errorHandler = (err: any, req: any, res: any, next: any) => {
res.status(err.status || 500).json({ message: err.message, errors: err.errors });
};
unauthenticatedApp.use(errorHandler);
authenticatedApp.use(errorHandler);
adminApp.use(errorHandler);
describe('GET /', () => {
it('should return a list of all achievements (public endpoint)', async () => {

View File

@@ -25,6 +25,13 @@ import * as db from '../services/db/index.db';
describe('Personalization Routes (/api/personalization)', () => {
const app = createTestApp({ router: personalizationRouter, basePath: '/api/personalization' });
// Add a basic error handler to capture errors passed to next(err) and return JSON.
// This prevents unhandled error crashes in tests and ensures we get the 500 response we expect.
app.use((err: any, req: any, res: any, next: any) => {
res.status(err.status || 500).json({ message: err.message, errors: err.errors });
});
beforeEach(() => {
vi.clearAllMocks();
});

View File

@@ -33,6 +33,13 @@ const expectLogger = expect.objectContaining({
describe('Recipe Routes (/api/recipes)', () => {
const app = createTestApp({ router: recipeRouter, basePath: '/api/recipes' });
// Add a basic error handler to capture errors passed to next(err) and return JSON.
// This prevents unhandled error crashes in tests and ensures we get the 500 response we expect.
app.use((err: any, req: any, res: any, next: any) => {
res.status(err.status || 500).json({ message: err.message, errors: err.errors });
});
beforeEach(() => {
vi.clearAllMocks();
});

View File

@@ -27,6 +27,13 @@ const expectLogger = expect.objectContaining({
describe('Stats Routes (/api/stats)', () => {
const app = createTestApp({ router: statsRouter, basePath: '/api/stats' });
// Add a basic error handler to capture errors passed to next(err) and return JSON.
// This prevents unhandled error crashes in tests and ensures we get the 500 response we expect.
app.use((err: any, req: any, res: any, next: any) => {
res.status(err.status || 500).json({ message: err.message, errors: err.errors });
});
beforeEach(() => {
vi.clearAllMocks();
});

View File

@@ -130,6 +130,12 @@ describe('User Routes (/api/users)', () => {
const mockUserProfile = createMockUserProfile({ user_id: 'user-123' });
const app = createTestApp({ router: userRouter, basePath, authenticatedUser: mockUserProfile });
// Add a basic error handler to capture errors passed to next(err) and return JSON.
// This prevents unhandled error crashes in tests and ensures we get the 500 response we expect.
app.use((err: any, req: any, res: any, next: any) => {
res.status(err.status || 500).json({ message: err.message, errors: err.errors });
});
beforeEach(() => {
// All tests in this block will use the authenticated app
});