Files
flyer-crawler.projectium.com/src/routes/admin.system.routes.test.ts
Torben Sorensen 2075ed199b Complete ADR-008 Phase 1: API Versioning Strategy
Implement URI-based API versioning with /api/v1 prefix across all routes.
This establishes a foundation for future API evolution and breaking changes.

Changes:
- server.ts: All routes mounted under /api/v1/ (15 route handlers)
- apiClient.ts: Base URL updated to /api/v1
- swagger.ts: OpenAPI server URL changed to /api/v1
- Redirect middleware: Added backwards compatibility for /api/* → /api/v1/*
- Tests: Updated 72 test files with versioned path assertions
- ADR documentation: Marked Phase 1 as complete (Accepted status)

Test fixes:
- apiClient.test.ts: 27 tests updated for /api/v1 paths
- user.routes.ts: 36 log messages updated to reflect versioned paths
- swagger.test.ts: 1 test updated for new server URL
- All integration/E2E tests updated for versioned endpoints

All Phase 1 acceptance criteria met:
✓ Routes use /api/v1/ prefix
✓ Frontend requests /api/v1/
✓ OpenAPI docs reflect /api/v1/
✓ Backwards compatibility via redirect middleware
✓ Tests pass with versioned paths

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-26 21:23:25 -08:00

124 lines
4.1 KiB
TypeScript

// src/routes/admin.system.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 { createTestApp } from '../tests/utils/createTestApp';
// Mock dependencies
vi.mock('../services/geocodingService.server', () => ({
geocodingService: {
clearGeocodeCache: vi.fn(),
},
}));
// Mock other dependencies that are part of the adminRouter setup but not directly tested here
vi.mock('../services/db/index.db', () => ({
adminRepo: {},
flyerRepo: {},
recipeRepo: {},
userRepo: {},
personalizationRepo: {},
notificationRepo: {},
}));
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', () => ({
backgroundJobService: {
runDailyDealCheck: vi.fn(),
},
}));
vi.mock('../services/queueService.server');
vi.mock('../services/queues.server');
vi.mock('../services/workers.server');
vi.mock('../services/monitoringService.server');
vi.mock('../services/cacheService.server');
vi.mock('../services/userService');
vi.mock('../services/brandService');
vi.mock('../services/receiptService.server');
vi.mock('../services/aiService.server');
vi.mock('../config/env', () => ({
config: {
database: { host: 'localhost', port: 5432, user: 'test', password: 'test', name: 'test' },
redis: { url: 'redis://localhost:6379' },
auth: { jwtSecret: 'test-secret' },
server: { port: 3000, host: 'localhost' },
},
isAiConfigured: vi.fn().mockReturnValue(false),
parseConfig: vi.fn(),
}));
vi.mock('@bull-board/api');
vi.mock('@bull-board/api/bullMQAdapter');
vi.mock('@bull-board/express', () => ({
ExpressAdapter: class {
setBasePath = vi.fn();
getRouter = vi
.fn()
.mockReturnValue((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 { geocodingService } from '../services/geocodingService.server';
// Mock the logger
vi.mock('../services/logger.server', async () => {
const { mockLogger, createMockLogger } = await import('../tests/utils/mockLogger');
return {
logger: mockLogger,
createScopedLogger: vi.fn(() => createMockLogger()),
};
});
// Mock the passport middleware
// Note: admin.routes.ts imports from '../config/passport', so we mock that path
vi.mock('../config/passport', () => ({
default: {
authenticate: vi.fn(() => (req: Request, res: Response, next: NextFunction) => {
req.user = createMockUserProfile({
role: 'admin',
user: { user_id: 'admin-user-id', email: 'admin@test.com' },
});
next();
}),
},
isAdmin: (req: Request, res: Response, next: NextFunction) => next(),
}));
describe('Admin System Routes (/api/v1/admin/system)', () => {
const adminUser = createMockUserProfile({
role: 'admin',
user: { user_id: 'admin-user-id', email: 'admin@test.com' },
});
const app = createTestApp({
router: adminRouter,
basePath: '/api/v1/admin',
authenticatedUser: adminUser,
});
beforeEach(() => {
vi.clearAllMocks();
});
describe('POST /system/clear-geocode-cache', () => {
it('should return 200 on successful cache clear', async () => {
vi.mocked(geocodingService.clearGeocodeCache).mockResolvedValue(10);
const response = await supertest(app).post('/api/v1/admin/system/clear-geocode-cache');
expect(response.status).toBe(200);
expect(response.body.data.message).toContain('10 keys were removed');
});
it('should return 500 if clearing the cache fails', async () => {
vi.mocked(geocodingService.clearGeocodeCache).mockRejectedValue(new Error('Redis is down'));
const response = await supertest(app).post('/api/v1/admin/system/clear-geocode-cache');
expect(response.status).toBe(500);
expect(response.body.error.message).toContain('Redis is down');
});
});
});