Files
flyer-crawler.projectium.com/src/routes/admin.stats.routes.test.ts

124 lines
4.1 KiB
TypeScript

// src/routes/admin.stats.routes.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import supertest from 'supertest';
import type { Request, Response, NextFunction } from 'express';
import { createMockUserProfile } from '../tests/utils/mockFactories';
import type { UserProfile } from '../types';
import { createTestApp } from '../tests/utils/createTestApp';
import { mockLogger } from '../tests/utils/mockLogger';
vi.mock('../services/db/index.db', () => ({
adminRepo: {
getApplicationStats: vi.fn(),
getDailyStatsForLast30Days: vi.fn(),
},
flyerRepo: {},
recipeRepo: {},
userRepo: {},
personalizationRepo: {},
notificationRepo: {},
}));
// Mock other dependencies
vi.mock('../services/db/flyer.db');
vi.mock('../services/db/recipe.db');
vi.mock('../services/db/user.db');
vi.mock('node:fs/promises');
vi.mock('../services/backgroundJobService');
vi.mock('../services/geocodingService.server');
vi.mock('../services/queueService.server');
vi.mock('@bull-board/api');
vi.mock('@bull-board/api/bullMQAdapter');
vi.mock('@bull-board/express', () => ({
ExpressAdapter: class {
setBasePath() {}
getRouter() {
return (req: Request, res: Response, next: NextFunction) => next();
}
},
}));
// Import the router AFTER all mocks are defined.
import adminRouter from './admin.routes';
// Import the mocked modules to control them
import { adminRepo } from '../services/db/index.db';
// Mock the logger
vi.mock('../services/logger.server', () => ({
logger: mockLogger,
}));
// Mock the passport middleware
vi.mock('./passport.routes', () => ({
default: {
authenticate: vi.fn(() => (req: Request, res: Response, next: NextFunction) => {
if (!req.user) return res.status(401).json({ message: 'Unauthorized' });
next();
}),
},
isAdmin: (req: Request, res: Response, next: NextFunction) => {
const user = req.user as UserProfile | undefined;
if (user && user.role === 'admin') next();
else res.status(403).json({ message: 'Forbidden: Administrator access required.' });
},
}));
describe('Admin Stats Routes (/api/admin/stats)', () => {
const adminUser = createMockUserProfile({ role: '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,
});
beforeEach(() => {
vi.clearAllMocks();
});
describe('GET /stats', () => {
it('should return application stats on success', async () => {
const mockStats = {
flyerCount: 150,
userCount: 42,
flyerItemCount: 10000,
storeCount: 12,
pendingCorrectionCount: 5,
recipeCount: 50,
};
vi.mocked(adminRepo.getApplicationStats).mockResolvedValue(mockStats);
const response = await supertest(app).get('/api/admin/stats');
expect(response.status).toBe(200);
expect(response.body).toEqual(mockStats);
});
it('should return 500 if the database call fails', async () => {
vi.mocked(adminRepo.getApplicationStats).mockRejectedValue(new Error('DB Error'));
const response = await supertest(app).get('/api/admin/stats');
expect(response.status).toBe(500);
expect(response.body.message).toBe('DB Error');
});
});
describe('GET /stats/daily', () => {
it('should return daily stats on success', async () => {
const mockDailyStats = [
{ date: '2024-01-01', new_users: 5, new_flyers: 10 },
{ date: '2024-01-02', new_users: 3, new_flyers: 8 },
];
vi.mocked(adminRepo.getDailyStatsForLast30Days).mockResolvedValue(mockDailyStats);
const response = await supertest(app).get('/api/admin/stats/daily');
expect(response.status).toBe(200);
expect(response.body).toEqual(mockDailyStats);
});
it('should return 500 if the database call fails', async () => {
vi.mocked(adminRepo.getDailyStatsForLast30Days).mockRejectedValue(new Error('DB Error'));
const response = await supertest(app).get('/api/admin/stats/daily');
expect(response.status).toBe(500);
expect(response.body.message).toBe('DB Error');
});
});
});