189 lines
6.2 KiB
TypeScript
189 lines
6.2 KiB
TypeScript
import { Request, Response, NextFunction } from 'express';
|
|
|
|
// Mock JWTService before importing anything else
|
|
const mockJWTService = {
|
|
verify: jest.fn(),
|
|
refreshIfNeeded: jest.fn(),
|
|
create: jest.fn(),
|
|
shouldRefreshToken: jest.fn(),
|
|
test: jest.fn(),
|
|
};
|
|
|
|
jest.mock('../../../src/Application/Services/JWTService', () => {
|
|
return {
|
|
JWTService: jest.fn().mockImplementation(() => mockJWTService)
|
|
};
|
|
});
|
|
|
|
// Now import the middleware which will use the mocked JWTService
|
|
import { authRequired, adminRequired } from '../../../src/Application/Services/AuthMiddleware';
|
|
|
|
describe('AuthMiddleware', () => {
|
|
let mockRequest: Partial<Request>;
|
|
let mockResponse: Partial<Response>;
|
|
let mockNext: NextFunction;
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
|
|
mockRequest = {
|
|
cookies: {}
|
|
};
|
|
|
|
mockResponse = {
|
|
status: jest.fn().mockReturnThis(),
|
|
json: jest.fn().mockReturnThis(),
|
|
cookie: jest.fn()
|
|
};
|
|
|
|
mockNext = jest.fn();
|
|
});
|
|
|
|
describe('authRequired', () => {
|
|
it('should call next() when token is valid', () => {
|
|
// Arrange
|
|
const validPayload = {
|
|
userId: 'user-123',
|
|
authLevel: 0 as 0 | 1,
|
|
orgId: 'org-123'
|
|
};
|
|
|
|
mockJWTService.verify.mockReturnValue(validPayload);
|
|
mockJWTService.refreshIfNeeded.mockReturnValue(false); // Token doesn't need refresh
|
|
|
|
// Act
|
|
authRequired(mockRequest as Request, mockResponse as Response, mockNext);
|
|
|
|
// Assert
|
|
expect(mockJWTService.verify).toHaveBeenCalledWith(mockRequest);
|
|
expect(mockJWTService.refreshIfNeeded).toHaveBeenCalledWith(validPayload, mockResponse);
|
|
expect((mockRequest as any).user).toBe(validPayload);
|
|
expect(mockNext).toHaveBeenCalled();
|
|
expect(mockResponse.status).not.toHaveBeenCalled();
|
|
expect(mockResponse.json).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should return 401 when token is invalid', () => {
|
|
// Arrange
|
|
mockJWTService.verify.mockReturnValue(null);
|
|
|
|
// Act
|
|
authRequired(mockRequest as Request, mockResponse as Response, mockNext);
|
|
|
|
// Assert
|
|
expect(mockJWTService.verify).toHaveBeenCalledWith(mockRequest);
|
|
expect(mockJWTService.refreshIfNeeded).not.toHaveBeenCalled();
|
|
expect(mockNext).not.toHaveBeenCalled();
|
|
expect(mockResponse.status).toHaveBeenCalledWith(401);
|
|
expect(mockResponse.json).toHaveBeenCalledWith({ error: 'Unauthorized' });
|
|
});
|
|
|
|
it('should refresh token when needed', () => {
|
|
// Arrange
|
|
const validPayload = {
|
|
userId: 'user-123',
|
|
authLevel: 0 as 0 | 1,
|
|
orgId: 'org-123'
|
|
};
|
|
|
|
mockJWTService.verify.mockReturnValue(validPayload);
|
|
mockJWTService.refreshIfNeeded.mockReturnValue(true); // Token needs refresh
|
|
|
|
// Act
|
|
authRequired(mockRequest as Request, mockResponse as Response, mockNext);
|
|
|
|
// Assert
|
|
expect(mockJWTService.verify).toHaveBeenCalledWith(mockRequest);
|
|
expect(mockJWTService.refreshIfNeeded).toHaveBeenCalledWith(validPayload, mockResponse);
|
|
expect((mockRequest as any).user).toBe(validPayload);
|
|
expect(mockNext).toHaveBeenCalled();
|
|
expect(mockResponse.status).not.toHaveBeenCalled();
|
|
expect(mockResponse.json).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('adminRequired', () => {
|
|
it('should call next() when token is valid and user is admin', () => {
|
|
// Arrange
|
|
const adminPayload = {
|
|
userId: 'admin-123',
|
|
authLevel: 1 as 0 | 1,
|
|
orgId: 'org-123'
|
|
};
|
|
|
|
mockJWTService.verify.mockReturnValue(adminPayload);
|
|
mockJWTService.refreshIfNeeded.mockReturnValue(false);
|
|
|
|
// Act
|
|
adminRequired(mockRequest as Request, mockResponse as Response, mockNext);
|
|
|
|
// Assert
|
|
expect(mockJWTService.verify).toHaveBeenCalledWith(mockRequest);
|
|
expect(mockJWTService.refreshIfNeeded).toHaveBeenCalledWith(adminPayload, mockResponse);
|
|
expect((mockRequest as any).user).toBe(adminPayload);
|
|
expect(mockNext).toHaveBeenCalled();
|
|
expect(mockResponse.status).not.toHaveBeenCalled();
|
|
expect(mockResponse.json).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should return 403 when token is invalid', () => {
|
|
// Arrange
|
|
mockJWTService.verify.mockReturnValue(null);
|
|
|
|
// Act
|
|
adminRequired(mockRequest as Request, mockResponse as Response, mockNext);
|
|
|
|
// Assert
|
|
expect(mockJWTService.verify).toHaveBeenCalledWith(mockRequest);
|
|
expect(mockJWTService.refreshIfNeeded).not.toHaveBeenCalled();
|
|
expect(mockNext).not.toHaveBeenCalled();
|
|
expect(mockResponse.status).toHaveBeenCalledWith(403);
|
|
expect(mockResponse.json).toHaveBeenCalledWith({ error: 'Forbidden' });
|
|
});
|
|
|
|
it('should return 403 when user is not admin', () => {
|
|
// Arrange
|
|
const regularUserPayload = {
|
|
userId: 'user-123',
|
|
authLevel: 0 as 0 | 1,
|
|
orgId: 'org-123'
|
|
};
|
|
|
|
mockJWTService.verify.mockReturnValue(regularUserPayload);
|
|
|
|
// Act
|
|
adminRequired(mockRequest as Request, mockResponse as Response, mockNext);
|
|
|
|
// Assert
|
|
expect(mockJWTService.verify).toHaveBeenCalledWith(mockRequest);
|
|
expect(mockJWTService.refreshIfNeeded).not.toHaveBeenCalled();
|
|
expect(mockNext).not.toHaveBeenCalled();
|
|
expect(mockResponse.status).toHaveBeenCalledWith(403);
|
|
expect(mockResponse.json).toHaveBeenCalledWith({ error: 'Forbidden' });
|
|
});
|
|
|
|
it('should refresh token for valid admin user', () => {
|
|
// Arrange
|
|
const adminPayload = {
|
|
userId: 'admin-123',
|
|
authLevel: 1 as 0 | 1,
|
|
orgId: 'org-123'
|
|
};
|
|
|
|
mockJWTService.verify.mockReturnValue(adminPayload);
|
|
mockJWTService.refreshIfNeeded.mockReturnValue(true);
|
|
|
|
// Act
|
|
adminRequired(mockRequest as Request, mockResponse as Response, mockNext);
|
|
|
|
// Assert
|
|
expect(mockJWTService.verify).toHaveBeenCalledWith(mockRequest);
|
|
expect(mockJWTService.refreshIfNeeded).toHaveBeenCalledWith(adminPayload, mockResponse);
|
|
expect((mockRequest as any).user).toBe(adminPayload);
|
|
expect(mockNext).toHaveBeenCalled();
|
|
expect(mockResponse.status).not.toHaveBeenCalled();
|
|
expect(mockResponse.json).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|