updating unit and integration tests
This commit is contained in:
352
backend/tests/unit/services/UserService.test.js
Normal file
352
backend/tests/unit/services/UserService.test.js
Normal file
@@ -0,0 +1,352 @@
|
||||
const UserService = require('../../../services/UserService');
|
||||
const { User, UserAddress } = require('../../../models');
|
||||
const emailServices = require('../../../services/email');
|
||||
const logger = require('../../../utils/logger');
|
||||
|
||||
// Mock dependencies
|
||||
jest.mock('../../../models', () => ({
|
||||
User: {
|
||||
findByPk: jest.fn(),
|
||||
},
|
||||
UserAddress: {
|
||||
create: jest.fn(),
|
||||
findOne: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('../../../services/email', () => ({
|
||||
auth: {
|
||||
sendPersonalInfoChangedEmail: jest.fn().mockResolvedValue(),
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('../../../utils/logger', () => ({
|
||||
info: jest.fn(),
|
||||
error: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('UserService', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
process.env.NODE_ENV = 'test';
|
||||
});
|
||||
|
||||
describe('updateProfile', () => {
|
||||
const mockUser = {
|
||||
id: 'user-123',
|
||||
email: 'original@example.com',
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
address1: '123 Main St',
|
||||
address2: null,
|
||||
city: 'New York',
|
||||
state: 'NY',
|
||||
zipCode: '10001',
|
||||
country: 'USA',
|
||||
update: jest.fn().mockResolvedValue(),
|
||||
};
|
||||
|
||||
it('should update user profile successfully', async () => {
|
||||
User.findByPk
|
||||
.mockResolvedValueOnce(mockUser) // First call to find user
|
||||
.mockResolvedValueOnce({ ...mockUser, firstName: 'Jane' }); // Second call for return
|
||||
|
||||
const updateData = { firstName: 'Jane' };
|
||||
const result = await UserService.updateProfile('user-123', updateData);
|
||||
|
||||
expect(User.findByPk).toHaveBeenCalledWith('user-123');
|
||||
expect(mockUser.update).toHaveBeenCalledWith({ firstName: 'Jane' }, {});
|
||||
expect(result.firstName).toBe('Jane');
|
||||
});
|
||||
|
||||
it('should throw error when user not found', async () => {
|
||||
User.findByPk.mockResolvedValue(null);
|
||||
|
||||
await expect(UserService.updateProfile('non-existent', { firstName: 'Test' }))
|
||||
.rejects.toThrow('User not found');
|
||||
});
|
||||
|
||||
it('should trim email and ignore empty email', async () => {
|
||||
User.findByPk
|
||||
.mockResolvedValueOnce(mockUser)
|
||||
.mockResolvedValueOnce(mockUser);
|
||||
|
||||
await UserService.updateProfile('user-123', { email: ' new@example.com ' });
|
||||
|
||||
expect(mockUser.update).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ email: 'new@example.com' }),
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should not update email if empty string', async () => {
|
||||
User.findByPk
|
||||
.mockResolvedValueOnce(mockUser)
|
||||
.mockResolvedValueOnce(mockUser);
|
||||
|
||||
await UserService.updateProfile('user-123', { email: ' ' });
|
||||
|
||||
// Email should not be in the update call
|
||||
expect(mockUser.update).toHaveBeenCalledWith({}, {});
|
||||
});
|
||||
|
||||
it('should convert empty phone to null', async () => {
|
||||
User.findByPk
|
||||
.mockResolvedValueOnce(mockUser)
|
||||
.mockResolvedValueOnce(mockUser);
|
||||
|
||||
await UserService.updateProfile('user-123', { phone: '' });
|
||||
|
||||
expect(mockUser.update).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ phone: null }),
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should trim phone number', async () => {
|
||||
User.findByPk
|
||||
.mockResolvedValueOnce(mockUser)
|
||||
.mockResolvedValueOnce(mockUser);
|
||||
|
||||
await UserService.updateProfile('user-123', { phone: ' 555-1234 ' });
|
||||
|
||||
expect(mockUser.update).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ phone: '555-1234' }),
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should pass options to update call', async () => {
|
||||
User.findByPk
|
||||
.mockResolvedValueOnce(mockUser)
|
||||
.mockResolvedValueOnce(mockUser);
|
||||
|
||||
const mockTransaction = { id: 'tx-123' };
|
||||
await UserService.updateProfile('user-123', { firstName: 'Jane' }, { transaction: mockTransaction });
|
||||
|
||||
expect(mockUser.update).toHaveBeenCalledWith(
|
||||
expect.any(Object),
|
||||
{ transaction: mockTransaction }
|
||||
);
|
||||
});
|
||||
|
||||
it('should not send email notification in test environment', async () => {
|
||||
User.findByPk
|
||||
.mockResolvedValueOnce(mockUser)
|
||||
.mockResolvedValueOnce({ ...mockUser, firstName: 'Jane' });
|
||||
|
||||
await UserService.updateProfile('user-123', { firstName: 'Jane' });
|
||||
|
||||
// Email should not be sent in test environment
|
||||
expect(emailServices.auth.sendPersonalInfoChangedEmail).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should send email notification in production when personal info changes', async () => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
|
||||
User.findByPk
|
||||
.mockResolvedValueOnce(mockUser)
|
||||
.mockResolvedValueOnce({ ...mockUser, firstName: 'Jane' });
|
||||
|
||||
await UserService.updateProfile('user-123', { firstName: 'Jane' });
|
||||
|
||||
expect(emailServices.auth.sendPersonalInfoChangedEmail).toHaveBeenCalledWith(mockUser);
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
'Personal information changed notification sent',
|
||||
expect.objectContaining({
|
||||
userId: 'user-123',
|
||||
changedFields: ['firstName'],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle email notification failure gracefully', async () => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
emailServices.auth.sendPersonalInfoChangedEmail.mockRejectedValueOnce(
|
||||
new Error('Email service down')
|
||||
);
|
||||
|
||||
User.findByPk
|
||||
.mockResolvedValueOnce(mockUser)
|
||||
.mockResolvedValueOnce({ ...mockUser, email: 'new@example.com' });
|
||||
|
||||
// Should not throw despite email failure
|
||||
const result = await UserService.updateProfile('user-123', { email: 'new@example.com' });
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
'Failed to send personal information changed notification',
|
||||
expect.objectContaining({
|
||||
error: 'Email service down',
|
||||
userId: 'user-123',
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createUserAddress', () => {
|
||||
const mockUser = {
|
||||
id: 'user-123',
|
||||
email: 'user@example.com',
|
||||
};
|
||||
|
||||
const addressData = {
|
||||
label: 'Home',
|
||||
address1: '456 Oak Ave',
|
||||
city: 'Boston',
|
||||
state: 'MA',
|
||||
zipCode: '02101',
|
||||
country: 'USA',
|
||||
};
|
||||
|
||||
it('should create a new address successfully', async () => {
|
||||
const mockAddress = { id: 'addr-123', ...addressData, userId: 'user-123' };
|
||||
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
UserAddress.create.mockResolvedValue(mockAddress);
|
||||
|
||||
const result = await UserService.createUserAddress('user-123', addressData);
|
||||
|
||||
expect(User.findByPk).toHaveBeenCalledWith('user-123');
|
||||
expect(UserAddress.create).toHaveBeenCalledWith({
|
||||
...addressData,
|
||||
userId: 'user-123',
|
||||
});
|
||||
expect(result.id).toBe('addr-123');
|
||||
});
|
||||
|
||||
it('should throw error when user not found', async () => {
|
||||
User.findByPk.mockResolvedValue(null);
|
||||
|
||||
await expect(UserService.createUserAddress('non-existent', addressData))
|
||||
.rejects.toThrow('User not found');
|
||||
});
|
||||
|
||||
it('should send notification in production', async () => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
const mockAddress = { id: 'addr-123', ...addressData };
|
||||
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
UserAddress.create.mockResolvedValue(mockAddress);
|
||||
|
||||
await UserService.createUserAddress('user-123', addressData);
|
||||
|
||||
expect(emailServices.auth.sendPersonalInfoChangedEmail).toHaveBeenCalledWith(mockUser);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateUserAddress', () => {
|
||||
const mockAddress = {
|
||||
id: 'addr-123',
|
||||
userId: 'user-123',
|
||||
address1: '123 Old St',
|
||||
update: jest.fn().mockResolvedValue(),
|
||||
};
|
||||
|
||||
it('should update address successfully', async () => {
|
||||
UserAddress.findOne.mockResolvedValue(mockAddress);
|
||||
User.findByPk.mockResolvedValue({ id: 'user-123', email: 'user@example.com' });
|
||||
|
||||
const result = await UserService.updateUserAddress('user-123', 'addr-123', {
|
||||
address1: '789 New St',
|
||||
});
|
||||
|
||||
expect(UserAddress.findOne).toHaveBeenCalledWith({
|
||||
where: { id: 'addr-123', userId: 'user-123' },
|
||||
});
|
||||
expect(mockAddress.update).toHaveBeenCalledWith({ address1: '789 New St' });
|
||||
expect(result.id).toBe('addr-123');
|
||||
});
|
||||
|
||||
it('should throw error when address not found', async () => {
|
||||
UserAddress.findOne.mockResolvedValue(null);
|
||||
|
||||
await expect(
|
||||
UserService.updateUserAddress('user-123', 'non-existent', { address1: 'New' })
|
||||
).rejects.toThrow('Address not found');
|
||||
});
|
||||
|
||||
it('should send notification in production', async () => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
const mockUser = { id: 'user-123', email: 'user@example.com' };
|
||||
|
||||
UserAddress.findOne.mockResolvedValue(mockAddress);
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
|
||||
await UserService.updateUserAddress('user-123', 'addr-123', { city: 'Chicago' });
|
||||
|
||||
expect(emailServices.auth.sendPersonalInfoChangedEmail).toHaveBeenCalledWith(mockUser);
|
||||
});
|
||||
|
||||
it('should handle email failure gracefully', async () => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
emailServices.auth.sendPersonalInfoChangedEmail.mockRejectedValueOnce(
|
||||
new Error('Email failed')
|
||||
);
|
||||
|
||||
UserAddress.findOne.mockResolvedValue(mockAddress);
|
||||
User.findByPk.mockResolvedValue({ id: 'user-123', email: 'user@example.com' });
|
||||
|
||||
const result = await UserService.updateUserAddress('user-123', 'addr-123', { city: 'Chicago' });
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(logger.error).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteUserAddress', () => {
|
||||
const mockAddress = {
|
||||
id: 'addr-123',
|
||||
userId: 'user-123',
|
||||
destroy: jest.fn().mockResolvedValue(),
|
||||
};
|
||||
|
||||
it('should delete address successfully', async () => {
|
||||
UserAddress.findOne.mockResolvedValue(mockAddress);
|
||||
User.findByPk.mockResolvedValue({ id: 'user-123', email: 'user@example.com' });
|
||||
|
||||
await UserService.deleteUserAddress('user-123', 'addr-123');
|
||||
|
||||
expect(UserAddress.findOne).toHaveBeenCalledWith({
|
||||
where: { id: 'addr-123', userId: 'user-123' },
|
||||
});
|
||||
expect(mockAddress.destroy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should throw error when address not found', async () => {
|
||||
UserAddress.findOne.mockResolvedValue(null);
|
||||
|
||||
await expect(
|
||||
UserService.deleteUserAddress('user-123', 'non-existent')
|
||||
).rejects.toThrow('Address not found');
|
||||
});
|
||||
|
||||
it('should send notification in production', async () => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
const mockUser = { id: 'user-123', email: 'user@example.com' };
|
||||
|
||||
UserAddress.findOne.mockResolvedValue(mockAddress);
|
||||
User.findByPk.mockResolvedValue(mockUser);
|
||||
|
||||
await UserService.deleteUserAddress('user-123', 'addr-123');
|
||||
|
||||
expect(emailServices.auth.sendPersonalInfoChangedEmail).toHaveBeenCalledWith(mockUser);
|
||||
});
|
||||
|
||||
it('should handle email failure gracefully', async () => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
emailServices.auth.sendPersonalInfoChangedEmail.mockRejectedValueOnce(
|
||||
new Error('Email failed')
|
||||
);
|
||||
|
||||
UserAddress.findOne.mockResolvedValue(mockAddress);
|
||||
User.findByPk.mockResolvedValue({ id: 'user-123', email: 'user@example.com' });
|
||||
|
||||
// Should not throw
|
||||
await UserService.deleteUserAddress('user-123', 'addr-123');
|
||||
|
||||
expect(logger.error).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user