import { AdminUser } from '@core/admin/domain/entities/AdminUser'; import { UserRole } from '@core/admin/domain/value-objects/UserRole'; import { UserStatus } from '@core/admin/domain/value-objects/UserStatus'; import { InMemoryAdminUserRepository } from './InMemoryAdminUserRepository'; describe('InMemoryAdminUserRepository', () => { describe('TDD - Test First', () => { let repository: InMemoryAdminUserRepository; beforeEach(() => { repository = new InMemoryAdminUserRepository(); }); describe('create', () => { it('should create a new user', async () => { // Arrange const user = AdminUser.create({ id: 'user-123', email: 'test@example.com', displayName: 'Test User', roles: ['user'], status: 'active', }); // Act const result = await repository.create(user); // Assert expect(result).toStrictEqual(user); const found = await repository.findById(user.id); expect(found).toStrictEqual(user); }); it('should throw error when creating user with duplicate email', async () => { // Arrange const user1 = AdminUser.create({ id: 'user-1', email: 'test@example.com', displayName: 'User 1', roles: ['user'], status: 'active', }); const user2 = AdminUser.create({ id: 'user-2', email: 'test@example.com', displayName: 'User 2', roles: ['user'], status: 'active', }); await repository.create(user1); // Act & Assert await expect(repository.create(user2)).rejects.toThrow('Email already exists'); }); it('should throw error when creating user with duplicate ID', async () => { // Arrange const user1 = AdminUser.create({ id: 'user-1', email: 'user1@example.com', displayName: 'User 1', roles: ['user'], status: 'active', }); const user2 = AdminUser.create({ id: 'user-1', email: 'user2@example.com', displayName: 'User 2', roles: ['user'], status: 'active', }); await repository.create(user1); // Act & Assert await expect(repository.create(user2)).rejects.toThrow('User ID already exists'); }); }); describe('findById', () => { it('should find user by ID', async () => { // Arrange const user = AdminUser.create({ id: 'user-123', email: 'test@example.com', displayName: 'Test User', roles: ['user'], status: 'active', }); await repository.create(user); // Act const found = await repository.findById(user.id); // Assert expect(found).toStrictEqual(user); }); it('should return null for non-existent user', async () => { // Arrange const nonExistentId = AdminUser.create({ id: 'non-existent', email: 'dummy@example.com', displayName: 'Dummy', roles: ['user'], status: 'active', }).id; // Act const found = await repository.findById(nonExistentId); // Assert expect(found).toBeNull(); }); }); describe('findByEmail', () => { it('should find user by email', async () => { // Arrange const user = AdminUser.create({ id: 'user-123', email: 'test@example.com', displayName: 'Test User', roles: ['user'], status: 'active', }); await repository.create(user); // Act const found = await repository.findByEmail(user.email); // Assert expect(found).toStrictEqual(user); }); it('should return null for non-existent email', async () => { // Arrange const nonExistentEmail = AdminUser.create({ id: 'dummy', email: 'non-existent@example.com', displayName: 'Dummy', roles: ['user'], status: 'active', }).email; // Act const found = await repository.findByEmail(nonExistentEmail); // Assert expect(found).toBeNull(); }); }); describe('emailExists', () => { it('should return true when email exists', async () => { // Arrange const user = AdminUser.create({ id: 'user-123', email: 'test@example.com', displayName: 'Test User', roles: ['user'], status: 'active', }); await repository.create(user); // Act const exists = await repository.emailExists(user.email); // Assert expect(exists).toBe(true); }); it('should return false when email does not exist', async () => { // Arrange const nonExistentEmail = AdminUser.create({ id: 'dummy', email: 'non-existent@example.com', displayName: 'Dummy', roles: ['user'], status: 'active', }).email; // Act const exists = await repository.emailExists(nonExistentEmail); // Assert expect(exists).toBe(false); }); }); describe('list', () => { it('should return empty list when no users exist', async () => { // Act const result = await repository.list({}); // Assert expect(result.users).toEqual([]); expect(result.total).toBe(0); expect(result.page).toBe(1); expect(result.limit).toBe(10); expect(result.totalPages).toBe(0); }); it('should return all users when no filters provided', async () => { // Arrange const user1 = AdminUser.create({ id: 'user-1', email: 'user1@example.com', displayName: 'User 1', roles: ['user'], status: 'active', }); const user2 = AdminUser.create({ id: 'user-2', email: 'user2@example.com', displayName: 'User 2', roles: ['admin'], status: 'active', }); await repository.create(user1); await repository.create(user2); // Act const result = await repository.list({}); // Assert expect(result.users).toHaveLength(2); expect(result.total).toBe(2); expect(result.page).toBe(1); expect(result.limit).toBe(10); expect(result.totalPages).toBe(1); }); it('should filter by role', async () => { // Arrange const user1 = AdminUser.create({ id: 'user-1', email: 'user1@example.com', displayName: 'User 1', roles: ['user'], status: 'active', }); const admin1 = AdminUser.create({ id: 'admin-1', email: 'admin1@example.com', displayName: 'Admin 1', roles: ['admin'], status: 'active', }); await repository.create(user1); await repository.create(admin1); // Act const result = await repository.list({ filter: { role: UserRole.fromString('admin') }, }); // Assert expect(result.users).toHaveLength(1); expect(result.users[0]?.id.value).toBe('admin-1'); expect(result.total).toBe(1); }); it('should filter by status', async () => { // Arrange const activeUser = AdminUser.create({ id: 'user-1', email: 'active@example.com', displayName: 'Active User', roles: ['user'], status: 'active', }); const suspendedUser = AdminUser.create({ id: 'user-2', email: 'suspended@example.com', displayName: 'Suspended User', roles: ['user'], status: 'suspended', }); await repository.create(activeUser); await repository.create(suspendedUser); // Act const result = await repository.list({ filter: { status: UserStatus.fromString('suspended') }, }); // Assert expect(result.users).toHaveLength(1); expect(result.users[0]?.id.value).toBe('user-2'); expect(result.total).toBe(1); }); it('should filter by email', async () => { // Arrange const user1 = AdminUser.create({ id: 'user-1', email: 'test@example.com', displayName: 'Test User', roles: ['user'], status: 'active', }); const user2 = AdminUser.create({ id: 'user-2', email: 'other@example.com', displayName: 'Other User', roles: ['user'], status: 'active', }); await repository.create(user1); await repository.create(user2); // Act const result = await repository.list({ filter: { email: user1.email }, }); // Assert expect(result.users).toHaveLength(1); expect(result.users[0]?.id.value).toBe('user-1'); expect(result.total).toBe(1); }); it('should filter by search (email or display name)', async () => { // Arrange const user1 = AdminUser.create({ id: 'user-1', email: 'search@example.com', displayName: 'Search User', roles: ['user'], status: 'active', }); const user2 = AdminUser.create({ id: 'user-2', email: 'other@example.com', displayName: 'Other User', roles: ['user'], status: 'active', }); await repository.create(user1); await repository.create(user2); // Act const result = await repository.list({ filter: { search: 'search' }, }); // Assert expect(result.users).toHaveLength(1); expect(result.users[0]?.id.value).toBe('user-1'); expect(result.total).toBe(1); }); it('should apply pagination', async () => { // Arrange - Create 15 users for (let i = 1; i <= 15; i++) { const user = AdminUser.create({ id: `user-${i}`, email: `user${i}@example.com`, displayName: `User ${i}`, roles: ['user'], status: 'active', }); await repository.create(user); } // Act - Get page 2 with limit 5 const result = await repository.list({ pagination: { page: 2, limit: 5 }, }); // Assert expect(result.users).toHaveLength(5); expect(result.total).toBe(15); expect(result.page).toBe(2); expect(result.limit).toBe(5); expect(result.totalPages).toBe(3); expect(result.users[0]?.id.value).toBe('user-6'); }); it('should sort by email ascending', async () => { // Arrange const user1 = AdminUser.create({ id: 'user-1', email: 'zebra@example.com', displayName: 'Zebra', roles: ['user'], status: 'active', }); const user2 = AdminUser.create({ id: 'user-2', email: 'alpha@example.com', displayName: 'Alpha', roles: ['user'], status: 'active', }); await repository.create(user1); await repository.create(user2); // Act const result = await repository.list({ sort: { field: 'email', direction: 'asc' }, }); // Assert expect(result.users).toHaveLength(2); expect(result.users[0]?.id.value).toBe('user-2'); expect(result.users[1]?.id.value).toBe('user-1'); }); it('should sort by display name descending', async () => { // Arrange const user1 = AdminUser.create({ id: 'user-1', email: 'user1@example.com', displayName: 'Zebra', roles: ['user'], status: 'active', }); const user2 = AdminUser.create({ id: 'user-2', email: 'user2@example.com', displayName: 'Alpha', roles: ['user'], status: 'active', }); await repository.create(user1); await repository.create(user2); // Act const result = await repository.list({ sort: { field: 'displayName', direction: 'desc' }, }); // Assert expect(result.users).toHaveLength(2); expect(result.users[0]?.id.value).toBe('user-1'); expect(result.users[1]?.id.value).toBe('user-2'); }); it('should apply multiple filters together', async () => { // Arrange const matchingUser = AdminUser.create({ id: 'user-1', email: 'admin@example.com', displayName: 'Admin User', roles: ['admin'], status: 'active', }); const nonMatchingUser1 = AdminUser.create({ id: 'user-2', email: 'user@example.com', displayName: 'User', roles: ['user'], status: 'active', }); const nonMatchingUser2 = AdminUser.create({ id: 'user-3', email: 'admin-suspended@example.com', displayName: 'Admin User', roles: ['admin'], status: 'suspended', }); await repository.create(matchingUser); await repository.create(nonMatchingUser1); await repository.create(nonMatchingUser2); // Act const result = await repository.list({ filter: { role: UserRole.fromString('admin'), status: UserStatus.fromString('active'), search: 'admin', }, }); // Assert expect(result.users).toHaveLength(1); expect(result.users[0]?.id.value).toBe('user-1'); expect(result.total).toBe(1); }); }); describe('count', () => { it('should return 0 when no users exist', async () => { // Act const count = await repository.count(); // Assert expect(count).toBe(0); }); it('should return correct count of all users', async () => { // Arrange const user1 = AdminUser.create({ id: 'user-1', email: 'user1@example.com', displayName: 'User 1', roles: ['user'], status: 'active', }); const user2 = AdminUser.create({ id: 'user-2', email: 'user2@example.com', displayName: 'User 2', roles: ['admin'], status: 'active', }); await repository.create(user1); await repository.create(user2); // Act const count = await repository.count(); // Assert expect(count).toBe(2); }); it('should count with filters', async () => { // Arrange const activeUser = AdminUser.create({ id: 'user-1', email: 'active@example.com', displayName: 'Active User', roles: ['user'], status: 'active', }); const suspendedUser = AdminUser.create({ id: 'user-2', email: 'suspended@example.com', displayName: 'Suspended User', roles: ['user'], status: 'suspended', }); await repository.create(activeUser); await repository.create(suspendedUser); // Act const count = await repository.count({ status: UserStatus.fromString('active'), }); // Assert expect(count).toBe(1); }); }); describe('update', () => { it('should update existing user', async () => { // Arrange const user = AdminUser.create({ id: 'user-1', email: 'test@example.com', displayName: 'Test User', roles: ['user'], status: 'active', }); await repository.create(user); // Act user.updateDisplayName('Updated Name'); const updated = await repository.update(user); // Assert expect(updated.displayName).toBe('Updated Name'); const found = await repository.findById(user.id); expect(found?.displayName).toBe('Updated Name'); }); it('should throw error when updating non-existent user', async () => { // Arrange const user = AdminUser.create({ id: 'non-existent', email: 'test@example.com', displayName: 'Test User', roles: ['user'], status: 'active', }); // Act & Assert await expect(repository.update(user)).rejects.toThrow('User not found'); }); it('should throw error when updating with duplicate email', async () => { // Arrange const user1 = AdminUser.create({ id: 'user-1', email: 'user1@example.com', displayName: 'User 1', roles: ['user'], status: 'active', }); const user2 = AdminUser.create({ id: 'user-2', email: 'user2@example.com', displayName: 'User 2', roles: ['user'], status: 'active', }); await repository.create(user1); await repository.create(user2); // Act - Try to change user2's email to user1's email user2.updateEmail(user1.email); // Assert await expect(repository.update(user2)).rejects.toThrow('Email already exists'); }); }); describe('delete', () => { it('should delete existing user', async () => { // Arrange const user = AdminUser.create({ id: 'user-1', email: 'test@example.com', displayName: 'Test User', roles: ['user'], status: 'active', }); await repository.create(user); // Act await repository.delete(user.id); // Assert const found = await repository.findById(user.id); expect(found).toBeNull(); }); it('should throw error when deleting non-existent user', async () => { // Arrange const nonExistentId = AdminUser.create({ id: 'non-existent', email: 'test@example.com', displayName: 'Test User', roles: ['user'], status: 'active', }).id; // Act & Assert await expect(repository.delete(nonExistentId)).rejects.toThrow('User not found'); }); it('should reduce count after deletion', async () => { // Arrange const user = AdminUser.create({ id: 'user-1', email: 'test@example.com', displayName: 'Test User', roles: ['user'], status: 'active', }); await repository.create(user); const initialCount = await repository.count(); // Act await repository.delete(user.id); // Assert const finalCount = await repository.count(); expect(finalCount).toBe(initialCount - 1); }); }); describe('integration scenarios', () => { it('should handle complete user lifecycle', async () => { // Arrange - Create user const user = AdminUser.create({ id: 'user-1', email: 'lifecycle@example.com', displayName: 'Lifecycle User', roles: ['user'], status: 'active', }); // Act - Create await repository.create(user); let found = await repository.findById(user.id); expect(found).toStrictEqual(user); // Act - Update user.updateDisplayName('Updated Lifecycle'); user.addRole(UserRole.fromString('admin')); await repository.update(user); found = await repository.findById(user.id); expect(found?.displayName).toBe('Updated Lifecycle'); expect(found?.hasRole('admin')).toBe(true); // Act - Delete await repository.delete(user.id); found = await repository.findById(user.id); expect(found).toBeNull(); }); it('should handle complex filtering and pagination', async () => { // Arrange - Create mixed users const users = [ AdminUser.create({ id: 'owner-1', email: 'owner@example.com', displayName: 'Owner User', roles: ['owner'], status: 'active', }), AdminUser.create({ id: 'admin-1', email: 'admin1@example.com', displayName: 'Admin One', roles: ['admin'], status: 'active', }), AdminUser.create({ id: 'admin-2', email: 'admin2@example.com', displayName: 'Admin Two', roles: ['admin'], status: 'suspended', }), AdminUser.create({ id: 'user-1', email: 'user1@example.com', displayName: 'User One', roles: ['user'], status: 'active', }), AdminUser.create({ id: 'user-2', email: 'user2@example.com', displayName: 'User Two', roles: ['user'], status: 'active', }), ]; for (const user of users) { await repository.create(user); } // Act - Get active admins, sorted by email, page 1, limit 2 const result = await repository.list({ filter: { role: UserRole.fromString('admin'), status: UserStatus.fromString('active'), }, sort: { field: 'email', direction: 'asc' }, pagination: { page: 1, limit: 2 }, }); // Assert expect(result.users).toHaveLength(1); expect(result.users[0]?.id.value).toBe('admin-1'); expect(result.total).toBe(1); expect(result.totalPages).toBe(1); }); }); }); });