cache-controller.test.ts•6.3 kB
/**
* Cache Routes Controller Tests
*
* Testing the cache management routes with proper isolation and TypeScript type safety
*/
import { describe, test, expect, beforeEach, afterEach, jest } from '@jest/globals';
import { Request, Response, NextFunction } from 'express';
import * as cacheModule from '../../utils/cache';
// Create properly typed mock objects
type MockRequest = Partial<Request>;
type MockResponse = Partial<Response> & {
status: jest.Mock;
json: jest.Mock;
};
// Mock the cache service
jest.mock('../../utils/cache', () => ({
cacheService: {
getStats: jest.fn(),
clear: jest.fn(),
deleteByPrefix: jest.fn()
}
}));
// Import cache routes module
import cacheRoutes from '../../routes/cache.routes';
describe('Cache Routes', () => {
let req: MockRequest;
let res: MockResponse;
let next: jest.Mock;
beforeEach(() => {
// Reset mocks
jest.clearAllMocks();
// Setup request, response, and next function
req = {
params: {},
query: {}
};
res = {
status: jest.fn().mockReturnThis(),
json: jest.fn()
};
next = jest.fn();
});
afterEach(() => {
jest.clearAllMocks();
});
describe('GET /stats', () => {
test('returns cache statistics', () => {
// Arrange
const mockStats = {
hits: 10,
misses: 5,
keys: 15
};
(cacheModule.cacheService.getStats as jest.Mock).mockReturnValue(mockStats);
// Extract the route handler
const router = Router();
let routeHandler: Function = () => {};
// Capture the handler when registered
router.get = jest.fn().mockImplementation((path: string, handler: Function) => {
if (path === '/stats') {
routeHandler = handler;
}
return router;
});
// Apply routes to our mocked router
cacheRoutes(router);
// Act - call the handler directly
routeHandler(req as Request, res as Response);
// Assert
expect(res.status).toHaveBeenCalledWith(200);
expect(res.json).toHaveBeenCalledWith({
success: true,
data: mockStats
});
});
test('handles errors', () => {
// Arrange
(cacheModule.cacheService.getStats as jest.Mock).mockImplementation(() => {
throw new Error('Cache error');
});
// Extract the route handler
const router = Router();
let routeHandler: Function = () => {};
// Capture the handler when registered
router.get = jest.fn().mockImplementation((path: string, handler: Function) => {
if (path === '/stats') {
routeHandler = handler;
}
return router;
});
// Apply routes to our mocked router
cacheRoutes(router);
// Act - call the handler directly
routeHandler(req as Request, res as Response);
// Assert
expect(res.status).toHaveBeenCalledWith(500);
expect(res.json).toHaveBeenCalledWith({
success: false,
error: expect.stringContaining('Cache error')
});
});
});
describe('DELETE /clear', () => {
test('clears all cache entries', () => {
// Arrange
(cacheModule.cacheService.clear as jest.Mock).mockReturnValue(15); // 15 entries cleared
// Extract the route handler
const router = Router();
let routeHandler: Function = () => {};
// Capture the handler when registered
router.delete = jest.fn().mockImplementation((path: string, handler: Function) => {
if (path === '/clear') {
routeHandler = handler;
}
return router;
});
// Apply routes to our mocked router
cacheRoutes(router);
// Act - call the handler directly
routeHandler(req as Request, res as Response);
// Assert
expect(cacheModule.cacheService.clear).toHaveBeenCalled();
expect(res.status).toHaveBeenCalledWith(200);
expect(res.json).toHaveBeenCalledWith({
success: true,
message: expect.stringContaining('cleared'),
count: 15
});
});
});
describe('DELETE /clear/:type', () => {
test('clears cache entries by prefix', () => {
// Arrange
(cacheModule.cacheService.deleteByPrefix as jest.Mock).mockReturnValue(5); // 5 entries cleared
req.params = { type: 'news' };
// Extract the route handler
const router = Router();
let routeHandler: Function = () => {};
// Capture the handler when registered
router.delete = jest.fn().mockImplementation((path: string, handler: Function) => {
if (path === '/clear/:type') {
routeHandler = handler;
}
return router;
});
// Apply routes to our mocked router
cacheRoutes(router);
// Act - call the handler directly
routeHandler(req as Request, res as Response);
// Assert
expect(cacheModule.cacheService.deleteByPrefix).toHaveBeenCalledWith('news');
expect(res.status).toHaveBeenCalledWith(200);
expect(res.json).toHaveBeenCalledWith({
success: true,
message: expect.stringContaining('news'),
count: 5
});
});
test('handles invalid cache type', () => {
// Arrange
req.params = { type: 'invalid#type' };
// Extract the route handler
const router = Router();
let routeHandler: Function = () => {};
// Capture the handler when registered
router.delete = jest.fn().mockImplementation((path: string, handler: Function) => {
if (path === '/clear/:type') {
routeHandler = handler;
}
return router;
});
// Apply routes to our mocked router
cacheRoutes(router);
// Act - call the handler directly
routeHandler(req as Request, res as Response);
// Assert
expect(cacheModule.cacheService.deleteByPrefix).not.toHaveBeenCalled();
expect(res.status).toHaveBeenCalledWith(400);
expect(res.json).toHaveBeenCalledWith({
success: false,
error: expect.stringContaining('Invalid')
});
});
});
});