import { describe, it, expect, vi, beforeEach } from 'vitest'; import { NextRequest } from 'next/server'; // Mock rate limiting - must be before route import vi.mock('@/lib/rate-limit', () => ({ withRateLimit: vi.fn().mockResolvedValue({ allowed: true, remaining: 100, limit: 120, resetAt: Date.now() + 60000 }), createRateLimitResponse: vi.fn(), createRateLimitHeaders: vi.fn().mockReturnValue({}), })); // Mock cache - must be before route import vi.mock('@/lib/cache', () => ({ getCached: vi.fn().mockResolvedValue(null), setCache: vi.fn().mockResolvedValue(undefined), cacheKeys: { stats: () => 'stats' }, cacheTTL: { stats: 3600 }, })); // Mock the db module - must be before imports that use it vi.mock('@skillhub/db', () => { const mockStatsRow = [{ totalSkills: 100, totalContributors: 50 }]; const mockDownloadsRow = [{ totalDownloads: 5000 }]; const mockCategoryRow = [{ count: 8 }]; return { createDb: vi.fn(() => ({ select: vi.fn().mockReturnValue({ from: vi.fn((table: unknown) => { // categories table query returns count directly (no .where()) if (table === 'categories-table') { return Promise.resolve(mockCategoryRow); } // skills table: thenable for downloads (no .where()) + .where() for filtered stats return { where: vi.fn().mockResolvedValue(mockStatsRow), then: (resolve: (v: unknown) => void, reject: (e: unknown) => void) => Promise.resolve(mockDownloadsRow).then(resolve, reject), }; }), }), })), skills: { downloadCount: 'download_count', githubOwner: 'github_owner', isDuplicate: 'is_duplicate', skillType: 'skill_type' }, categories: 'categories-table', sql: vi.fn((..._args: unknown[]) => 'mock-sql'), }; }); import { GET } from './route'; // Helper to create mock request function createMockRequest(url = 'http://localhost:3000/api/stats') { return new NextRequest(url); } describe('GET /api/stats', () => { beforeEach(() => { vi.clearAllMocks(); }); it('should return totalSkills count', async () => { const request = createMockRequest(); const response = await GET(request); const data = await response.json(); expect(response.status).toBe(200); expect(data.totalSkills).toBeDefined(); expect(typeof data.totalSkills).toBe('number'); }); it('should return totalDownloads sum', async () => { const request = createMockRequest(); const response = await GET(request); const data = await response.json(); expect(data.totalDownloads).toBeDefined(); expect(typeof data.totalDownloads).toBe('number'); }); it('should return totalCategories count', async () => { const request = createMockRequest(); const response = await GET(request); const data = await response.json(); expect(data.totalCategories).toBeDefined(); expect(typeof data.totalCategories).toBe('number'); }); it('should return totalContributors count', async () => { const request = createMockRequest(); const response = await GET(request); const data = await response.json(); expect(data.totalContributors).toBeDefined(); expect(typeof data.totalContributors).toBe('number'); }); it('should return platforms count', async () => { const request = createMockRequest(); const response = await GET(request); const data = await response.json(); expect(data.platforms).toBe(5); }); });