routes
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m28s

This commit is contained in:
2025-12-05 01:36:27 -08:00
parent e0af06f54c
commit e08dd0231a
4 changed files with 53 additions and 64 deletions

View File

@@ -2,7 +2,7 @@
import { describe, it, expect, vi, beforeEach, afterAll, type Mocked, type Mock } from 'vitest';
import supertest from 'supertest';
import express, { Request, Response, NextFunction } from 'express';
import path from 'node:path';
import path from 'path';
import fs from 'node:fs/promises';
import adminRouter from './admin.routes'; // Correctly imported
import { UserProfile } from '../types';
@@ -31,32 +31,25 @@ vi.mock('../services/logger.server', () => ({
},
}));
/**
* Mock the passport authentication middleware.
* This is the core of testing protected routes. We replace the real authentication
* with a flexible mock that we can control in each test.
*/
// Use vi.hoisted to create a mutable mock function reference that can be controlled in tests.
const mockedIsAdmin = vi.hoisted(() => vi.fn());
vi.mock('./passport', () => ({
// Mock the default export (the passport instance)
default: {
// The 'authenticate' method returns a middleware function. We mock that.
authenticate: vi.fn(() => (req: Request, res: Response, next: NextFunction) => {
// This mock will be controlled by the isAdmin mock, but we ensure it calls next()
// to allow the isAdmin middleware to run.
// For admin tests, we just need to ensure the request passes through to the isAdmin middleware.
next();
}),
},
// Mock the named export 'isAdmin'
isAdmin: vi.fn((_req: Request, res: Response) => {
// The default behavior of this mock is to deny access.
// We will override this implementation in specific tests.
res.status(401).json({ message: 'Unauthorized' });
}),
// Mock the named export 'isAdmin' by assigning our hoisted mock function to it.
isAdmin: mockedIsAdmin,
}));
// We need to import the mocked 'isAdmin' so we can change its behavior in tests.
import { isAdmin } from './passport.routes';
const mockedIsAdmin = isAdmin as Mock;
// We no longer need to import `isAdmin` from the real module, as we control `mockedIsAdmin` directly.
// import { isAdmin } from './passport.routes'; // This line is removed.
// const mockedIsAdmin = isAdmin as Mock; // This line is removed.
// Create a minimal Express app to host our router
const app = express();

View File

@@ -26,22 +26,19 @@ vi.mock('../services/logger.server', () => ({
},
}));
// Use vi.hoisted to create a mutable mock function reference for the authenticate middleware.
const mockedAuthenticate = vi.hoisted(() => vi.fn((_req: Request, _res: Response, next: NextFunction) => next()));
// Mock the passport module to control authentication for different tests.
vi.mock('./passport', () => ({
// Mock the default export for passport.authenticate
default: {
authenticate: vi.fn(() => (_req: Request, _res: Response, next: NextFunction) => {
next(); // Immediately pass through for testing purposes
}),
authenticate: mockedAuthenticate,
},
// Mock the named export for optionalAuth
optionalAuth: vi.fn((req, res, next) => next()),
}));
// We need to import the mocked passport object to control its behavior in tests.
import passport from './passport.routes';
const mockedAuthenticate = vi.mocked(passport.authenticate);
// Create a minimal Express app to host our router
const app = express();
app.use(express.json({ strict: false }));
@@ -51,11 +48,13 @@ describe('AI Routes (/api/ai)', () => {
beforeEach(() => {
vi.clearAllMocks();
// Default mock for passport.authenticate to simulate an unauthenticated request.
// This will be overridden in tests that require an authenticated user.
mockedAuthenticate.mockImplementation(
() => (req: Request, res: Response) => {
// We use mockImplementation to return a function that returns the middleware.
// This correctly mimics passport.authenticate('jwt', options) returning a middleware function.
mockedAuthenticate.mockImplementation(() => {
return (req: Request, res: Response) => {
res.status(401).json({ message: 'Unauthorized' });
});
};
});
});
describe('POST /flyers/process', () => {
@@ -201,12 +200,12 @@ describe('AI Routes (/api/ai)', () => {
beforeEach(() => {
// For this block, simulate a logged-in user by having the middleware call next().
mockedAuthenticate.mockImplementation(
() => (req: Request, res: Response, next: NextFunction) => {
mockedAuthenticate.mockImplementation(() => {
return (req: Request, res: Response, next: NextFunction) => {
req.user = mockUserProfile;
next();
}
);
};
});
});
it('POST /quick-insights should return the stubbed response', async () => {

View File

@@ -20,21 +20,19 @@ vi.mock('../services/logger.server', () => ({
},
}));
// Mock the passport authentication middleware
let mockAuthMiddleware = (req: Request, res: Response, next: NextFunction) => next();
// Use vi.hoisted to create mutable mock function references.
const mockedAuthMiddleware = vi.hoisted(() => vi.fn((req: Request, res: Response, next: NextFunction) => next()));
const mockedIsAdmin = vi.hoisted(() => vi.fn());
vi.mock('./passport', () => ({
default: {
authenticate: vi.fn(() => (req: Request, res: Response, next: NextFunction) => {
mockAuthMiddleware(req, res, next);
}),
// The authenticate method will now call our hoisted mock middleware.
authenticate: vi.fn(() => mockedAuthMiddleware),
},
// Mock the named export 'isAdmin'
isAdmin: vi.fn(),
isAdmin: mockedIsAdmin,
}));
import { isAdmin } from './passport.routes'; // Keep this for isAdmin
const mockedIsAdmin = vi.mocked(isAdmin);
// Create a minimal Express app to host our router
const app = express();
app.use(express.json({ strict: false }));
@@ -56,10 +54,10 @@ describe('Gamification Routes (/api/achievements)', () => {
beforeEach(() => {
vi.clearAllMocks();
// Default mock for unauthenticated user for protected routes
mockAuthMiddleware = (req: Request, res: Response) => {
// Default mock for authentication to simulate an unauthenticated user.
mockedAuthMiddleware.mockImplementation((req: Request, res: Response) => {
res.status(401).json({ message: 'Unauthorized' });
};
});
mockedIsAdmin.mockImplementation((req: Request, res: Response) => {
res.status(403).json({ message: 'Forbidden' });
});
@@ -88,11 +86,11 @@ describe('Gamification Routes (/api/achievements)', () => {
});
it('should return achievements for the authenticated user', async () => {
// Mock for authenticated user
mockAuthMiddleware = (req: Request, res: Response, next: NextFunction) => {
// Mock authentication to simulate a logged-in user.
mockedAuthMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => {
req.user = mockUserProfile;
next();
};
});
const mockUserAchievements: (UserAchievement & Achievement)[] = [{ achievement_id: 1, user_id: 'user-123', achieved_at: '2024-01-01', name: 'First Steps', description: '...', icon: 'footprints', points_value: 10 }];
mockedDb.getUserAchievements.mockResolvedValue(mockUserAchievements);
@@ -115,11 +113,11 @@ describe('Gamification Routes (/api/achievements)', () => {
it('should return 403 Forbidden if authenticated user is not an admin', async () => {
// Mock a regular authenticated user
mockAuthMiddleware = (req: Request, res: Response, next: NextFunction) => {
mockedAuthMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => {
req.user = mockUserProfile;
next();
};
// Let the default isAdmin mock run, which denies access
});
// Let the default isAdmin mock (set in beforeEach) run, which denies access
const response = await supertest(app).post('/api/achievements/award').send(awardPayload);
expect(response.status).toBe(403);
@@ -127,11 +125,11 @@ describe('Gamification Routes (/api/achievements)', () => {
it('should successfully award an achievement when user is an admin', async () => {
// Mock an authenticated admin user
mockAuthMiddleware = (req: Request, res: Response, next: NextFunction) => {
mockedAuthMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => {
req.user = mockAdminProfile;
next();
};
mockedIsAdmin.mockImplementation((req: Request, res: Response, next: NextFunction) => next());
});
mockedIsAdmin.mockImplementation((req: Request, res: Response, next: NextFunction) => next()); // Grant admin access
mockedDb.awardAchievement.mockResolvedValue(undefined);
const response = await supertest(app).post('/api/achievements/award').send(awardPayload);

View File

@@ -36,14 +36,13 @@ vi.mock('../services/logger.server', () => ({
},
}));
// Mock Passport middleware
let mockAuthMiddleware = (req: express.Request, res: express.Response, next: express.NextFunction) => next();
// Use vi.hoisted to create a mutable mock function reference for the authenticate middleware.
const mockedAuthMiddleware = vi.hoisted(() => vi.fn((req: express.Request, res: express.Response, next: express.NextFunction) => next()));
// Mock Passport middleware
vi.mock('./passport', () => ({
default: {
authenticate: vi.fn(() => (req: express.Request, res: express.Response, next: express.NextFunction) => {
mockAuthMiddleware(req, res, next);
}),
authenticate: vi.fn(() => mockedAuthMiddleware),
},
}));
@@ -56,10 +55,10 @@ describe('User Routes (/api/users)', () => {
beforeEach(() => {
vi.clearAllMocks();
// Default authentication state: Unauthorized
mockAuthMiddleware = (req: express.Request, res: express.Response) => {
// Default authentication state: Unauthorized by default for each test.
mockedAuthMiddleware.mockImplementation((req: express.Request, res: express.Response) => {
res.status(401).json({ message: 'Unauthorized' });
};
});
});
describe('when user is not authenticated', () => {
@@ -86,11 +85,11 @@ describe('User Routes (/api/users)', () => {
};
beforeEach(() => {
// Simulate logged-in user
mockAuthMiddleware = (req: express.Request, res: express.Response, next: express.NextFunction) => {
// Simulate a logged-in user for this block of tests.
mockedAuthMiddleware.mockImplementation((req: express.Request, res: express.Response, next: express.NextFunction) => {
req.user = mockUserProfile;
next();
};
});
});
describe('GET /profile', () => {