-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
384 additions
and
0 deletions.
There are no files selected for viewing
160 changes: 160 additions & 0 deletions
160
backend/src/database/seeds/create-admin-user.seed.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
import { DataSource, Repository, FindOneOptions } from 'typeorm'; | ||
import { User, UserRole } from '../../users/entities/user.entity'; | ||
import { createAdminUser } from './create-admin-user.seed'; | ||
import * as bcrypt from 'bcrypt'; | ||
|
||
jest.mock('bcrypt'); | ||
|
||
describe('createAdminUser', () => { | ||
let mockDataSource: Partial<DataSource>; | ||
let mockUserRepository: Partial<Repository<User>>; | ||
const originalEnv = process.env; | ||
|
||
beforeEach(() => { | ||
// Mock repository methods | ||
mockUserRepository = { | ||
findOne: jest.fn() as jest.Mock, | ||
create: jest.fn() as jest.Mock, | ||
save: jest.fn() as jest.Mock, | ||
}; | ||
|
||
// Mock DataSource | ||
mockDataSource = { | ||
getRepository: jest.fn().mockReturnValue(mockUserRepository), | ||
}; | ||
|
||
// Mock bcrypt | ||
(bcrypt.hash as jest.Mock).mockResolvedValue('hashed-password'); | ||
|
||
// Setup test environment variables | ||
process.env = { | ||
...originalEnv, | ||
ADMIN_EMAIL: '[email protected]', | ||
ADMIN_PASSWORD: 'admin123', | ||
}; | ||
|
||
// Mock console methods | ||
jest.spyOn(console, 'log').mockImplementation(() => {}); | ||
jest.spyOn(console, 'error').mockImplementation(() => {}); | ||
}); | ||
|
||
afterEach(() => { | ||
process.env = originalEnv; | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('should create admin user when it does not exist', async () => { | ||
// Mock that admin doesn't exist | ||
(mockUserRepository.findOne as jest.Mock).mockResolvedValue(null); | ||
|
||
// Mock created user | ||
const mockCreatedUser = { | ||
email: '[email protected]', | ||
password: 'hashed-password', | ||
firstName: 'Admin', | ||
lastName: 'User', | ||
role: UserRole.ADMIN, | ||
}; | ||
(mockUserRepository.create as jest.Mock).mockReturnValue(mockCreatedUser); | ||
(mockUserRepository.save as jest.Mock).mockResolvedValue(mockCreatedUser); | ||
|
||
await createAdminUser(mockDataSource as DataSource); | ||
|
||
// Verify admin user was searched for | ||
expect(mockUserRepository.findOne).toHaveBeenCalledWith({ | ||
where: { email: '[email protected]' }, | ||
}); | ||
|
||
// Verify password was hashed | ||
expect(bcrypt.hash).toHaveBeenCalledWith('admin123', 10); | ||
|
||
// Verify user was created with correct data | ||
expect(mockUserRepository.create).toHaveBeenCalledWith({ | ||
email: '[email protected]', | ||
password: 'hashed-password', | ||
firstName: 'Admin', | ||
lastName: 'User', | ||
role: UserRole.ADMIN, | ||
}); | ||
|
||
// Verify user was saved | ||
expect(mockUserRepository.save).toHaveBeenCalledWith(mockCreatedUser); | ||
|
||
// Verify success message was logged | ||
expect(console.log).toHaveBeenCalledWith('Admin user created successfully'); | ||
}); | ||
|
||
it('should not create admin user when it already exists', async () => { | ||
// Mock that admin already exists | ||
const existingAdmin = { | ||
email: '[email protected]', | ||
role: UserRole.ADMIN, | ||
}; | ||
(mockUserRepository.findOne as jest.Mock).mockResolvedValue(existingAdmin); | ||
|
||
await createAdminUser(mockDataSource as DataSource); | ||
|
||
// Verify admin user was searched for | ||
expect(mockUserRepository.findOne).toHaveBeenCalledWith({ | ||
where: { email: '[email protected]' }, | ||
}); | ||
|
||
// Verify no creation attempts were made | ||
expect(mockUserRepository.create).not.toHaveBeenCalled(); | ||
expect(mockUserRepository.save).not.toHaveBeenCalled(); | ||
|
||
// Verify appropriate message was logged | ||
expect(console.log).toHaveBeenCalledWith('Admin user already exists'); | ||
}); | ||
|
||
it('should throw error when admin email is missing', async () => { | ||
delete process.env.ADMIN_EMAIL; | ||
|
||
await expect(createAdminUser(mockDataSource as DataSource)).rejects.toThrow( | ||
'Admin email and password must be set in environment variables', | ||
); | ||
|
||
// Verify no database operations were attempted | ||
expect(mockUserRepository.findOne).not.toHaveBeenCalled(); | ||
expect(mockUserRepository.create).not.toHaveBeenCalled(); | ||
expect(mockUserRepository.save).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('should throw error when admin password is missing', async () => { | ||
delete process.env.ADMIN_PASSWORD; | ||
|
||
await expect(createAdminUser(mockDataSource as DataSource)).rejects.toThrow( | ||
'Admin email and password must be set in environment variables', | ||
); | ||
|
||
// Verify no database operations were attempted | ||
expect(mockUserRepository.findOne).not.toHaveBeenCalled(); | ||
expect(mockUserRepository.create).not.toHaveBeenCalled(); | ||
expect(mockUserRepository.save).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('should handle database errors', async () => { | ||
// Mock database error | ||
const dbError = new Error('Database connection failed'); | ||
(mockUserRepository.findOne as jest.Mock).mockRejectedValue(dbError); | ||
|
||
await expect(createAdminUser(mockDataSource as DataSource)).rejects.toThrow(dbError); | ||
|
||
// Verify error was logged | ||
expect(console.error).toHaveBeenCalledWith('Error creating admin user:', dbError); | ||
}); | ||
|
||
it('should handle password hashing errors', async () => { | ||
// Mock that admin doesn't exist | ||
(mockUserRepository.findOne as jest.Mock).mockResolvedValue(null); | ||
|
||
// Mock bcrypt error | ||
const hashError = new Error('Hashing failed'); | ||
(bcrypt.hash as jest.Mock).mockRejectedValue(hashError); | ||
|
||
await expect(createAdminUser(mockDataSource as DataSource)).rejects.toThrow(hashError); | ||
|
||
// Verify error was logged | ||
expect(console.error).toHaveBeenCalledWith('Error creating admin user:', hashError); | ||
}); | ||
}); |
224 changes: 224 additions & 0 deletions
224
backend/src/database/seeds/create-initial-data.seed.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
import { DataSource, Repository, InsertQueryBuilder } from 'typeorm'; | ||
import { User, UserRole } from '../../users/entities/user.entity'; | ||
import { Employee } from '../../employees/entities/employee.entity'; | ||
import { Service } from '../../services/entities/service.entity'; | ||
import { createInitialData } from './create-initial-data.seed'; | ||
import * as bcrypt from 'bcrypt'; | ||
|
||
jest.mock('bcrypt'); | ||
|
||
describe('createInitialData', () => { | ||
let mockDataSource: Partial<DataSource>; | ||
let mockUserRepository: Partial<Repository<User>>; | ||
let mockEmployeeRepository: Partial<Repository<Employee>>; | ||
let mockServiceRepository: Partial<Repository<Service>>; | ||
let mockQueryBuilder: Partial<InsertQueryBuilder<any>>; | ||
const originalEnv = process.env; | ||
|
||
beforeEach(() => { | ||
// Mock repositories | ||
mockUserRepository = { | ||
findOne: jest.fn() as jest.Mock, | ||
save: jest.fn() as jest.Mock, | ||
}; | ||
|
||
mockEmployeeRepository = { | ||
save: jest.fn() as jest.Mock, | ||
}; | ||
|
||
mockServiceRepository = { | ||
save: jest.fn() as jest.Mock, | ||
}; | ||
|
||
// Mock query builder for service associations | ||
mockQueryBuilder = { | ||
insert: jest.fn().mockReturnThis(), | ||
into: jest.fn().mockReturnThis(), | ||
values: jest.fn().mockReturnThis(), | ||
execute: jest.fn().mockResolvedValue(undefined), | ||
}; | ||
|
||
// Mock DataSource | ||
mockDataSource = { | ||
getRepository: jest.fn((entity) => { | ||
if (entity === User) return mockUserRepository; | ||
if (entity === Employee) return mockEmployeeRepository; | ||
if (entity === Service) return mockServiceRepository; | ||
return {} as Repository<any>; | ||
}), | ||
createQueryBuilder: jest.fn().mockReturnValue(mockQueryBuilder), | ||
}; | ||
|
||
// Mock bcrypt | ||
(bcrypt.hash as jest.Mock).mockResolvedValue('hashed-password'); | ||
|
||
// Setup test environment variables | ||
process.env = { | ||
...originalEnv, | ||
EMPLOYEE_EMAIL: '[email protected]', | ||
EMPLOYEE_PASSWORD: 'employee123', | ||
EMPLOYEE_PHONE: '+1234567890', | ||
}; | ||
|
||
// Mock console methods | ||
jest.spyOn(console, 'log').mockImplementation(() => {}); | ||
jest.spyOn(console, 'error').mockImplementation(() => {}); | ||
}); | ||
|
||
afterEach(() => { | ||
process.env = originalEnv; | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('should create services, employee user, and employee when they do not exist', async () => { | ||
// Mock that employee doesn't exist | ||
(mockUserRepository.findOne as jest.Mock).mockResolvedValue(null); | ||
|
||
// Mock created services | ||
const mockServices = [ | ||
{ id: '1', name: 'Haircut' }, | ||
{ id: '2', name: 'Hair Coloring' }, | ||
{ id: '3', name: 'Styling' }, | ||
]; | ||
(mockServiceRepository.save as jest.Mock).mockResolvedValue(mockServices); | ||
|
||
// Mock created employee user | ||
const mockEmployeeUser = { | ||
id: 'user-1', | ||
email: '[email protected]', | ||
role: UserRole.EMPLOYEE, | ||
}; | ||
(mockUserRepository.save as jest.Mock).mockResolvedValue(mockEmployeeUser); | ||
|
||
// Mock created employee | ||
const mockEmployee = { | ||
id: 'employee-1', | ||
user: mockEmployeeUser, | ||
}; | ||
(mockEmployeeRepository.save as jest.Mock).mockResolvedValue(mockEmployee); | ||
|
||
await createInitialData(mockDataSource as DataSource); | ||
|
||
// Verify services were created | ||
expect(mockServiceRepository.save).toHaveBeenCalledWith([ | ||
expect.objectContaining({ name: 'Haircut' }), | ||
expect.objectContaining({ name: 'Hair Coloring' }), | ||
expect.objectContaining({ name: 'Styling' }), | ||
]); | ||
|
||
// Verify employee user was created | ||
expect(mockUserRepository.save).toHaveBeenCalledWith(expect.objectContaining({ | ||
email: '[email protected]', | ||
role: UserRole.EMPLOYEE, | ||
})); | ||
|
||
// Verify employee was created | ||
expect(mockEmployeeRepository.save).toHaveBeenCalledWith(expect.objectContaining({ | ||
user: mockEmployeeUser, | ||
specializations: ['haircut', 'coloring'], | ||
})); | ||
|
||
// Verify services were associated with employee | ||
expect(mockQueryBuilder.insert).toHaveBeenCalled(); | ||
expect(mockQueryBuilder.into).toHaveBeenCalledWith('employee_services'); | ||
expect(mockQueryBuilder.values).toHaveBeenCalledWith( | ||
mockServices.map(service => ({ | ||
employee_id: mockEmployee.id, | ||
service_id: service.id, | ||
})), | ||
); | ||
|
||
// Verify success message was logged | ||
expect(console.log).toHaveBeenCalledWith('Initial data seeded successfully'); | ||
}); | ||
|
||
it('should not create employee when it already exists', async () => { | ||
// Mock that employee already exists | ||
const existingEmployee = { | ||
email: '[email protected]', | ||
role: UserRole.EMPLOYEE, | ||
}; | ||
(mockUserRepository.findOne as jest.Mock).mockResolvedValue(existingEmployee); | ||
|
||
await createInitialData(mockDataSource as DataSource); | ||
|
||
// Verify services were still created | ||
expect(mockServiceRepository.save).toHaveBeenCalled(); | ||
|
||
// Verify no employee creation attempts were made | ||
expect(mockUserRepository.save).not.toHaveBeenCalled(); | ||
expect(mockEmployeeRepository.save).not.toHaveBeenCalled(); | ||
expect(mockQueryBuilder.insert).not.toHaveBeenCalled(); | ||
|
||
// Verify appropriate message was logged | ||
expect(console.log).toHaveBeenCalledWith('Employee user already exists'); | ||
}); | ||
|
||
it('should throw error when employee email is missing', async () => { | ||
delete process.env.EMPLOYEE_EMAIL; | ||
|
||
await expect(createInitialData(mockDataSource as DataSource)).rejects.toThrow( | ||
'Employee email and password must be set in environment variables', | ||
); | ||
|
||
// Verify no database operations were attempted | ||
expect(mockServiceRepository.save).not.toHaveBeenCalled(); | ||
expect(mockUserRepository.findOne).not.toHaveBeenCalled(); | ||
expect(mockUserRepository.save).not.toHaveBeenCalled(); | ||
expect(mockEmployeeRepository.save).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('should throw error when employee password is missing', async () => { | ||
delete process.env.EMPLOYEE_PASSWORD; | ||
|
||
await expect(createInitialData(mockDataSource as DataSource)).rejects.toThrow( | ||
'Employee email and password must be set in environment variables', | ||
); | ||
|
||
// Verify no database operations were attempted | ||
expect(mockServiceRepository.save).not.toHaveBeenCalled(); | ||
expect(mockUserRepository.findOne).not.toHaveBeenCalled(); | ||
expect(mockUserRepository.save).not.toHaveBeenCalled(); | ||
expect(mockEmployeeRepository.save).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('should handle database errors during service creation', async () => { | ||
const dbError = new Error('Database error during service creation'); | ||
(mockServiceRepository.save as jest.Mock).mockRejectedValue(dbError); | ||
|
||
await expect(createInitialData(mockDataSource as DataSource)).rejects.toThrow(dbError); | ||
|
||
// Verify error was logged | ||
expect(console.error).toHaveBeenCalledWith('Error creating initial data:', dbError); | ||
}); | ||
|
||
it('should handle database errors during employee creation', async () => { | ||
// Mock that employee doesn't exist | ||
(mockUserRepository.findOne as jest.Mock).mockResolvedValue(null); | ||
// Mock services creation success | ||
(mockServiceRepository.save as jest.Mock).mockResolvedValue([]); | ||
// Mock employee creation error | ||
const dbError = new Error('Database error during employee creation'); | ||
(mockUserRepository.save as jest.Mock).mockRejectedValue(dbError); | ||
|
||
await expect(createInitialData(mockDataSource as DataSource)).rejects.toThrow(dbError); | ||
|
||
// Verify error was logged | ||
expect(console.error).toHaveBeenCalledWith('Error creating initial data:', dbError); | ||
}); | ||
|
||
it('should handle password hashing errors', async () => { | ||
// Mock that employee doesn't exist | ||
(mockUserRepository.findOne as jest.Mock).mockResolvedValue(null); | ||
// Mock services creation success | ||
(mockServiceRepository.save as jest.Mock).mockResolvedValue([]); | ||
// Mock bcrypt error | ||
const hashError = new Error('Hashing failed'); | ||
(bcrypt.hash as jest.Mock).mockRejectedValue(hashError); | ||
|
||
await expect(createInitialData(mockDataSource as DataSource)).rejects.toThrow(hashError); | ||
|
||
// Verify error was logged | ||
expect(console.error).toHaveBeenCalledWith('Error creating initial data:', hashError); | ||
}); | ||
}); |