// 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'; 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', async () => ({ // Use async import to avoid hoisting issues with mockLogger logger: (await import('../tests/utils/mockLogger')).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'); }); }); });