negyedik gyakorlat + megoldasok
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
const authMiddleware = require('../../../src/api/middlewares/authMiddleware');
|
||||
const JwtService = require('../../../src/application/services/JwtService');
|
||||
|
||||
// Mock JwtService
|
||||
jest.mock('../../../src/application/services/JwtService');
|
||||
|
||||
describe('authMiddleware (Cookie-based)', () => {
|
||||
let mockReq;
|
||||
let mockRes;
|
||||
let mockNext;
|
||||
let mockJwtService;
|
||||
|
||||
beforeEach(() => {
|
||||
// Mock Express req/res/next
|
||||
mockReq = {
|
||||
cookies: {}
|
||||
};
|
||||
mockRes = {
|
||||
status: jest.fn().mockReturnThis(),
|
||||
json: jest.fn()
|
||||
};
|
||||
mockNext = jest.fn();
|
||||
|
||||
// Mock JwtService instance
|
||||
mockJwtService = {
|
||||
extractTokenFromCookies: jest.fn(),
|
||||
verifyToken: jest.fn()
|
||||
};
|
||||
|
||||
JwtService.mockImplementation(() => mockJwtService);
|
||||
|
||||
// Reset all mocks
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('successful authentication', () => {
|
||||
it('should authenticate valid JWT token from cookie and call next()', () => {
|
||||
// Arrange
|
||||
mockReq.cookies = { auth_token: 'valid_token_123' };
|
||||
|
||||
const mockDecoded = {
|
||||
userId: 1,
|
||||
email: 'john@example.com'
|
||||
};
|
||||
|
||||
mockJwtService.extractTokenFromCookies.mockReturnValue('valid_token_123');
|
||||
mockJwtService.verifyToken.mockReturnValue(mockDecoded);
|
||||
|
||||
// Act
|
||||
authMiddleware(mockReq, mockRes, mockNext);
|
||||
|
||||
// Assert
|
||||
expect(mockReq.user).toEqual({
|
||||
userId: 1,
|
||||
email: 'john@example.com'
|
||||
});
|
||||
expect(mockNext).toHaveBeenCalled();
|
||||
expect(mockRes.status).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('authentication failures', () => {
|
||||
it('should return 401 if no cookie is present', () => {
|
||||
// Arrange
|
||||
mockReq.cookies = {};
|
||||
|
||||
mockJwtService.extractTokenFromCookies.mockReturnValue(null);
|
||||
|
||||
// Act
|
||||
authMiddleware(mockReq, mockRes, mockNext);
|
||||
|
||||
// Assert
|
||||
expect(mockRes.status).toHaveBeenCalledWith(401);
|
||||
expect(mockRes.json).toHaveBeenCalledWith({
|
||||
error: 'Authentication required',
|
||||
message: 'No token provided in cookies'
|
||||
});
|
||||
expect(mockNext).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return 401 if cookie token is invalid', () => {
|
||||
// Arrange
|
||||
mockReq.cookies = { auth_token: 'invalid_token' };
|
||||
|
||||
mockJwtService.extractTokenFromCookies.mockReturnValue('invalid_token');
|
||||
mockJwtService.verifyToken.mockImplementation(() => {
|
||||
throw new Error('Invalid or expired token');
|
||||
});
|
||||
|
||||
// Act
|
||||
authMiddleware(mockReq, mockRes, mockNext);
|
||||
|
||||
// Assert
|
||||
expect(mockRes.status).toHaveBeenCalledWith(401);
|
||||
expect(mockRes.json).toHaveBeenCalledWith({
|
||||
error: 'Authentication failed',
|
||||
message: 'Invalid or expired token'
|
||||
});
|
||||
expect(mockNext).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return 401 if token is expired', () => {
|
||||
// Arrange
|
||||
mockReq.cookies = { auth_token: 'expired_token' };
|
||||
|
||||
mockJwtService.extractTokenFromCookies.mockReturnValue('expired_token');
|
||||
mockJwtService.verifyToken.mockImplementation(() => {
|
||||
throw new Error('Token has expired');
|
||||
});
|
||||
|
||||
// Act
|
||||
authMiddleware(mockReq, mockRes, mockNext);
|
||||
|
||||
// Assert
|
||||
expect(mockRes.status).toHaveBeenCalledWith(401);
|
||||
expect(mockRes.json).toHaveBeenCalledWith({
|
||||
error: 'Authentication failed',
|
||||
message: 'Token has expired'
|
||||
});
|
||||
expect(mockNext).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,156 @@
|
||||
const corsMiddleware = require('../../../src/api/middlewares/corsMiddleware');
|
||||
|
||||
describe('CORS Middleware', () => {
|
||||
let req, res, next;
|
||||
|
||||
beforeEach(() => {
|
||||
req = {
|
||||
headers: {},
|
||||
method: 'GET',
|
||||
};
|
||||
res = {
|
||||
setHeader: jest.fn(),
|
||||
status: jest.fn().mockReturnThis(),
|
||||
end: jest.fn(),
|
||||
};
|
||||
next = jest.fn();
|
||||
});
|
||||
|
||||
describe('Allowed origins', () => {
|
||||
test('should allow requests from http://localhost:3001', () => {
|
||||
req.headers.origin = 'http://localhost:3001';
|
||||
|
||||
corsMiddleware(req, res, next);
|
||||
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Origin', 'http://localhost:3001');
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Credentials', 'true');
|
||||
expect(next).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should allow requests from http://localhost:3000', () => {
|
||||
req.headers.origin = 'http://localhost:3000';
|
||||
|
||||
corsMiddleware(req, res, next);
|
||||
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Origin', 'http://localhost:3000');
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Credentials', 'true');
|
||||
expect(next).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should allow requests from https://myapp.com', () => {
|
||||
req.headers.origin = 'https://myapp.com';
|
||||
|
||||
corsMiddleware(req, res, next);
|
||||
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Origin', 'https://myapp.com');
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Credentials', 'true');
|
||||
expect(next).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Disallowed origins', () => {
|
||||
test('should reject requests from unknown origins', () => {
|
||||
req.headers.origin = 'http://malicious-site.com';
|
||||
|
||||
corsMiddleware(req, res, next);
|
||||
|
||||
expect(res.status).toHaveBeenCalledWith(403);
|
||||
expect(res.end).toHaveBeenCalledWith('CORS policy: Origin not allowed');
|
||||
expect(next).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should reject requests from http://evil.com', () => {
|
||||
req.headers.origin = 'http://evil.com';
|
||||
|
||||
corsMiddleware(req, res, next);
|
||||
|
||||
expect(res.status).toHaveBeenCalledWith(403);
|
||||
expect(res.end).toHaveBeenCalledWith('CORS policy: Origin not allowed');
|
||||
expect(next).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Preflight requests (OPTIONS)', () => {
|
||||
beforeEach(() => {
|
||||
req.method = 'OPTIONS';
|
||||
});
|
||||
|
||||
test('should handle OPTIONS request from allowed origin', () => {
|
||||
req.headers.origin = 'http://localhost:3001';
|
||||
|
||||
corsMiddleware(req, res, next);
|
||||
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Origin', 'http://localhost:3001');
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Credentials', 'true');
|
||||
expect(res.status).toHaveBeenCalledWith(204);
|
||||
expect(res.end).toHaveBeenCalled();
|
||||
expect(next).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should reject OPTIONS request from disallowed origin', () => {
|
||||
req.headers.origin = 'http://hackersite.com';
|
||||
|
||||
corsMiddleware(req, res, next);
|
||||
|
||||
expect(res.status).toHaveBeenCalledWith(403);
|
||||
expect(res.end).toHaveBeenCalledWith('CORS policy: Origin not allowed');
|
||||
expect(next).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('No origin header', () => {
|
||||
test('should allow requests without origin header (same-origin)', () => {
|
||||
delete req.headers.origin;
|
||||
|
||||
corsMiddleware(req, res, next);
|
||||
|
||||
expect(next).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Headers configuration', () => {
|
||||
test('should set correct CORS headers for allowed origin', () => {
|
||||
req.headers.origin = 'http://localhost:3000';
|
||||
|
||||
corsMiddleware(req, res, next);
|
||||
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Origin', 'http://localhost:3000');
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Credentials', 'true');
|
||||
});
|
||||
|
||||
test('should set method headers for OPTIONS request', () => {
|
||||
req.headers.origin = 'http://localhost:3000';
|
||||
req.method = 'OPTIONS';
|
||||
|
||||
corsMiddleware(req, res, next);
|
||||
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Real-world scenarios', () => {
|
||||
test('should handle POST request from allowed frontend', () => {
|
||||
req.headers.origin = 'http://localhost:3001';
|
||||
req.method = 'POST';
|
||||
|
||||
corsMiddleware(req, res, next);
|
||||
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Origin', 'http://localhost:3001');
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Credentials', 'true');
|
||||
expect(next).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should reject DELETE request from disallowed origin', () => {
|
||||
req.headers.origin = 'http://unauthorized.com';
|
||||
req.method = 'DELETE';
|
||||
|
||||
corsMiddleware(req, res, next);
|
||||
|
||||
expect(res.status).toHaveBeenCalledWith(403);
|
||||
expect(next).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user