338 lines
8.6 KiB
JavaScript
338 lines
8.6 KiB
JavaScript
const authController = require('../controllers/auth.controller');
|
|
const { User } = require('../models');
|
|
const bcrypt = require('bcrypt');
|
|
const jwt = require('jsonwebtoken');
|
|
|
|
// Mock dependencies
|
|
jest.mock('../models');
|
|
jest.mock('bcrypt');
|
|
jest.mock('jsonwebtoken');
|
|
|
|
describe('Auth Controller', () => {
|
|
let req, res;
|
|
|
|
beforeEach(() => {
|
|
req = {
|
|
body: {},
|
|
user: null
|
|
};
|
|
res = {
|
|
status: jest.fn().mockReturnThis(),
|
|
json: jest.fn().mockReturnThis()
|
|
};
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
describe('register', () => {
|
|
it('should register a new user successfully', async () => {
|
|
req.body = {
|
|
username: 'testuser',
|
|
email: 'test@example.com',
|
|
password: 'Test123!@#'
|
|
};
|
|
|
|
User.findOne = jest.fn().mockResolvedValue(null);
|
|
User.create = jest.fn().mockResolvedValue({
|
|
id: '123',
|
|
username: 'testuser',
|
|
email: 'test@example.com',
|
|
role: 'user'
|
|
});
|
|
jwt.sign = jest.fn().mockReturnValue('mock-token');
|
|
|
|
await authController.register(req, res);
|
|
|
|
expect(res.status).toHaveBeenCalledWith(201);
|
|
expect(res.json).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
success: true,
|
|
message: 'User registered successfully',
|
|
data: expect.objectContaining({
|
|
token: 'mock-token',
|
|
user: expect.objectContaining({
|
|
username: 'testuser',
|
|
email: 'test@example.com'
|
|
})
|
|
})
|
|
})
|
|
);
|
|
});
|
|
|
|
it('should return 400 if username already exists', async () => {
|
|
req.body = {
|
|
username: 'existinguser',
|
|
email: 'new@example.com',
|
|
password: 'Test123!@#'
|
|
};
|
|
|
|
User.findOne = jest.fn().mockResolvedValue({ username: 'existinguser' });
|
|
|
|
await authController.register(req, res);
|
|
|
|
expect(res.status).toHaveBeenCalledWith(400);
|
|
expect(res.json).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
success: false,
|
|
message: 'Username already exists'
|
|
})
|
|
);
|
|
});
|
|
|
|
it('should return 409 if email already exists', async () => {
|
|
req.body = {
|
|
username: 'newuser',
|
|
email: 'existing@example.com',
|
|
password: 'Test123!@#'
|
|
};
|
|
|
|
User.findOne = jest.fn()
|
|
.mockResolvedValueOnce(null) // username check
|
|
.mockResolvedValueOnce({ email: 'existing@example.com' }); // email check
|
|
|
|
await authController.register(req, res);
|
|
|
|
expect(res.status).toHaveBeenCalledWith(409);
|
|
expect(res.json).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
success: false,
|
|
message: 'Email already registered'
|
|
})
|
|
);
|
|
});
|
|
|
|
it('should return 400 for missing required fields', async () => {
|
|
req.body = {
|
|
username: 'testuser'
|
|
// missing email and password
|
|
};
|
|
|
|
await authController.register(req, res);
|
|
|
|
expect(res.status).toHaveBeenCalledWith(400);
|
|
expect(res.json).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
success: false
|
|
})
|
|
);
|
|
});
|
|
|
|
it('should return 500 on database error', async () => {
|
|
req.body = {
|
|
username: 'testuser',
|
|
email: 'test@example.com',
|
|
password: 'Test123!@#'
|
|
};
|
|
|
|
User.findOne = jest.fn().mockRejectedValue(new Error('Database error'));
|
|
|
|
await authController.register(req, res);
|
|
|
|
expect(res.status).toHaveBeenCalledWith(500);
|
|
expect(res.json).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
success: false,
|
|
message: 'Internal server error'
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('login', () => {
|
|
it('should login user successfully with email', async () => {
|
|
req.body = {
|
|
email: 'test@example.com',
|
|
password: 'Test123!@#'
|
|
};
|
|
|
|
const mockUser = {
|
|
id: '123',
|
|
username: 'testuser',
|
|
email: 'test@example.com',
|
|
password: 'hashed-password',
|
|
role: 'user',
|
|
isActive: true,
|
|
save: jest.fn()
|
|
};
|
|
|
|
User.findOne = jest.fn().mockResolvedValue(mockUser);
|
|
bcrypt.compare = jest.fn().mockResolvedValue(true);
|
|
jwt.sign = jest.fn().mockReturnValue('mock-token');
|
|
|
|
await authController.login(req, res);
|
|
|
|
expect(res.status).toHaveBeenCalledWith(200);
|
|
expect(res.json).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
success: true,
|
|
message: 'Login successful',
|
|
data: expect.objectContaining({
|
|
token: 'mock-token'
|
|
})
|
|
})
|
|
);
|
|
});
|
|
|
|
it('should login user successfully with username', async () => {
|
|
req.body = {
|
|
username: 'testuser',
|
|
password: 'Test123!@#'
|
|
};
|
|
|
|
const mockUser = {
|
|
id: '123',
|
|
username: 'testuser',
|
|
email: 'test@example.com',
|
|
password: 'hashed-password',
|
|
role: 'user',
|
|
isActive: true,
|
|
save: jest.fn()
|
|
};
|
|
|
|
User.findOne = jest.fn().mockResolvedValue(mockUser);
|
|
bcrypt.compare = jest.fn().mockResolvedValue(true);
|
|
jwt.sign = jest.fn().mockReturnValue('mock-token');
|
|
|
|
await authController.login(req, res);
|
|
|
|
expect(res.status).toHaveBeenCalledWith(200);
|
|
});
|
|
|
|
it('should return 400 if user not found', async () => {
|
|
req.body = {
|
|
email: 'nonexistent@example.com',
|
|
password: 'Test123!@#'
|
|
};
|
|
|
|
User.findOne = jest.fn().mockResolvedValue(null);
|
|
|
|
await authController.login(req, res);
|
|
|
|
expect(res.status).toHaveBeenCalledWith(400);
|
|
expect(res.json).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
success: false,
|
|
message: 'Invalid credentials'
|
|
})
|
|
);
|
|
});
|
|
|
|
it('should return 400 if password is incorrect', async () => {
|
|
req.body = {
|
|
email: 'test@example.com',
|
|
password: 'WrongPassword'
|
|
};
|
|
|
|
User.findOne = jest.fn().mockResolvedValue({
|
|
password: 'hashed-password'
|
|
});
|
|
bcrypt.compare = jest.fn().mockResolvedValue(false);
|
|
|
|
await authController.login(req, res);
|
|
|
|
expect(res.status).toHaveBeenCalledWith(400);
|
|
expect(res.json).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
success: false,
|
|
message: 'Invalid credentials'
|
|
})
|
|
);
|
|
});
|
|
|
|
it('should return 403 if user account is deactivated', async () => {
|
|
req.body = {
|
|
email: 'test@example.com',
|
|
password: 'Test123!@#'
|
|
};
|
|
|
|
User.findOne = jest.fn().mockResolvedValue({
|
|
isActive: false,
|
|
password: 'hashed-password'
|
|
});
|
|
bcrypt.compare = jest.fn().mockResolvedValue(true);
|
|
|
|
await authController.login(req, res);
|
|
|
|
expect(res.status).toHaveBeenCalledWith(403);
|
|
expect(res.json).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
success: false,
|
|
message: 'Account is deactivated'
|
|
})
|
|
);
|
|
});
|
|
|
|
it('should return 400 for missing credentials', async () => {
|
|
req.body = {};
|
|
|
|
await authController.login(req, res);
|
|
|
|
expect(res.status).toHaveBeenCalledWith(400);
|
|
});
|
|
});
|
|
|
|
describe('logout', () => {
|
|
it('should logout user successfully', async () => {
|
|
req.user = { id: '123' };
|
|
|
|
await authController.logout(req, res);
|
|
|
|
expect(res.status).toHaveBeenCalledWith(200);
|
|
expect(res.json).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
success: true,
|
|
message: 'Logged out successfully'
|
|
})
|
|
);
|
|
});
|
|
|
|
it('should handle logout without user context', async () => {
|
|
req.user = null;
|
|
|
|
await authController.logout(req, res);
|
|
|
|
expect(res.status).toHaveBeenCalledWith(200);
|
|
});
|
|
});
|
|
|
|
describe('verifyToken', () => {
|
|
it('should verify user successfully', async () => {
|
|
req.user = {
|
|
id: '123',
|
|
username: 'testuser',
|
|
email: 'test@example.com',
|
|
role: 'user'
|
|
};
|
|
|
|
await authController.verifyToken(req, res);
|
|
|
|
expect(res.status).toHaveBeenCalledWith(200);
|
|
expect(res.json).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
success: true,
|
|
message: 'Token is valid',
|
|
data: expect.objectContaining({
|
|
user: expect.objectContaining({
|
|
id: '123',
|
|
username: 'testuser'
|
|
})
|
|
})
|
|
})
|
|
);
|
|
});
|
|
|
|
it('should return 401 if no user in request', async () => {
|
|
req.user = null;
|
|
|
|
await authController.verifyToken(req, res);
|
|
|
|
expect(res.status).toHaveBeenCalledWith(401);
|
|
expect(res.json).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
success: false,
|
|
message: 'Unauthorized'
|
|
})
|
|
);
|
|
});
|
|
});
|
|
});
|