import { CreateContactCommandHandler } from '../../../../src/Application/Contact/commands/CreateContactCommandHandler'; import { UpdateContactCommandHandler } from '../../../../src/Application/Contact/commands/UpdateContactCommandHandler'; import { DeleteContactCommandHandler } from '../../../../src/Application/Contact/commands/DeleteContactCommandHandler'; import { CreateContactCommand } from '../../../../src/Application/Contact/commands/CreateContactCommand'; import { UpdateContactCommand } from '../../../../src/Application/Contact/commands/UpdateContactCommand'; import { DeleteContactCommand } from '../../../../src/Application/Contact/commands/DeleteContactCommand'; import { ContactType, ContactState } from '../../../../src/Domain/Contact/ContactAggregate'; import { createMockContactRepository, createMockContact } from '../../../testUtils'; describe('Contact Command Handlers - Comprehensive', () => { let mockContactRepository: ReturnType; beforeEach(() => { mockContactRepository = createMockContactRepository(); }); describe('CreateContactCommandHandler', () => { let handler: CreateContactCommandHandler; beforeEach(() => { handler = new CreateContactCommandHandler(mockContactRepository); }); it('should create contact successfully with all fields', async () => { // Arrange const mockContactData = createMockContact({ id: '550e8400-e29b-41d4-a716-446655440000', name: 'John Doe', email: 'john@example.com', userid: '123e4567-e89b-12d3-a456-426614174000', type: ContactType.QUESTION, txt: 'Test question', state: ContactState.ACTIVE }); mockContactRepository.create.mockResolvedValue(mockContactData); const command: CreateContactCommand = { name: 'John Doe', email: 'john@example.com', userid: '123e4567-e89b-12d3-a456-426614174000', type: ContactType.QUESTION, txt: 'Test question' }; // Act const result = await handler.execute(command); // Assert - Returns ShortContactDto expect(result).toEqual({ id: '550e8400-e29b-41d4-a716-446655440000', name: 'John Doe', email: 'john@example.com', type: ContactType.QUESTION, state: ContactState.ACTIVE, createDate: expect.any(Date) }); expect(mockContactRepository.create).toHaveBeenCalledWith( expect.objectContaining({ name: 'John Doe', email: 'john@example.com', userid: '123e4567-e89b-12d3-a456-426614174000', type: ContactType.QUESTION, txt: 'Test question', state: ContactState.ACTIVE }) ); }); it('should create contact without userid (anonymous)', async () => { // Arrange const mockContactData = createMockContact({ id: '550e8400-e29b-41d4-a716-446655440001', name: 'Anonymous User', email: 'anon@example.com', userid: null, type: ContactType.BUG, txt: 'Bug report', state: ContactState.ACTIVE }); mockContactRepository.create.mockResolvedValue(mockContactData); const command: CreateContactCommand = { name: 'Anonymous User', email: 'anon@example.com', type: ContactType.BUG, txt: 'Bug report' }; // Act const result = await handler.execute(command); // Assert expect(result).toEqual({ id: '550e8400-e29b-41d4-a716-446655440001', name: 'Anonymous User', email: 'anon@example.com', type: ContactType.BUG, state: ContactState.ACTIVE, createDate: expect.any(Date) }); expect(mockContactRepository.create).toHaveBeenCalledWith( expect.objectContaining({ userid: null }) ); }); it('should create contact with different contact types', async () => { const testCases = [ { type: ContactType.BUG, description: 'Bug report' }, { type: ContactType.PROBLEM, description: 'Problem report' }, { type: ContactType.QUESTION, description: 'Question' }, { type: ContactType.SALES, description: 'Sales inquiry' }, { type: ContactType.OTHER, description: 'Other inquiry' } ]; for (const testCase of testCases) { // Arrange const mockContactData = createMockContact({ type: testCase.type, txt: testCase.description }); mockContactRepository.create.mockResolvedValue(mockContactData); const command: CreateContactCommand = { name: 'Test User', email: 'test@example.com', type: testCase.type, txt: testCase.description }; // Act const result = await handler.execute(command); // Assert expect(result.type).toBe(testCase.type); expect(mockContactRepository.create).toHaveBeenCalledWith( expect.objectContaining({ txt: testCase.description }) ); } }); it('should handle database errors', async () => { // Arrange const command: CreateContactCommand = { name: 'Error User', email: 'error@example.com', type: ContactType.QUESTION, txt: 'This will cause an error' }; mockContactRepository.create.mockRejectedValue(new Error('Database error')); // Act & Assert await expect(handler.execute(command)).rejects.toThrow('Failed to create contact'); }); it('should handle non-Error exceptions', async () => { // Arrange const command: CreateContactCommand = { name: 'Exception User', email: 'exception@example.com', type: ContactType.QUESTION, txt: 'This will cause an exception' }; mockContactRepository.create.mockRejectedValue('String error'); // Act & Assert await expect(handler.execute(command)).rejects.toThrow('Failed to create contact'); }); }); describe('UpdateContactCommandHandler', () => { let handler: UpdateContactCommandHandler; beforeEach(() => { handler = new UpdateContactCommandHandler(mockContactRepository); }); it('should update contact with admin response', async () => { // Arrange const existingContact = createMockContact({ id: '550e8400-e29b-41d4-a716-446655440000', adminResponse: null, state: ContactState.ACTIVE }); const updatedContact = createMockContact({ ...existingContact, adminResponse: 'Thank you for your inquiry', state: ContactState.RESOLVED, responseDate: new Date(), respondedBy: 'admin123' }); mockContactRepository.findById.mockResolvedValue(existingContact); mockContactRepository.update.mockResolvedValue(updatedContact); const command: UpdateContactCommand = { id: '550e8400-e29b-41d4-a716-446655440000', adminResponse: 'Thank you for your inquiry' }; // Act const result = await handler.execute(command); // Assert - Returns DetailContactDto expect(result).toEqual({ id: '550e8400-e29b-41d4-a716-446655440000', name: expect.any(String), email: expect.any(String), userid: expect.any(String), type: expect.any(Number), txt: expect.any(String), state: ContactState.RESOLVED, createDate: expect.any(Date), updateDate: expect.any(Date), adminResponse: 'Thank you for your inquiry', responseDate: expect.any(Date), respondedBy: 'admin123' }); }); it('should update contact state', async () => { // Arrange const existingContact = createMockContact({ id: '550e8400-e29b-41d4-a716-446655440000', state: ContactState.ACTIVE }); const updatedContact = createMockContact({ ...existingContact, state: ContactState.RESOLVED }); mockContactRepository.findById.mockResolvedValue(existingContact); mockContactRepository.update.mockResolvedValue(updatedContact); const command: UpdateContactCommand = { id: '550e8400-e29b-41d4-a716-446655440000', state: ContactState.RESOLVED }; // Act const result = await handler.execute(command); // Assert expect(result.state).toBe(ContactState.RESOLVED); }); it('should throw error when contact not found', async () => { // Arrange mockContactRepository.findById.mockResolvedValue(null); const command: UpdateContactCommand = { id: 'non-existent-id', adminResponse: 'Response' }; // Act & Assert await expect(handler.execute(command)).rejects.toThrow('Contact not found'); }); it('should handle repository errors during update', async () => { // Arrange const existingContact = createMockContact(); mockContactRepository.findById.mockResolvedValue(existingContact); mockContactRepository.update.mockRejectedValue(new Error('Database error')); const command: UpdateContactCommand = { id: 'existing-id', adminResponse: 'Response' }; // Act & Assert await expect(handler.execute(command)).rejects.toThrow('Failed to update contact'); }); }); describe('DeleteContactCommandHandler', () => { let handler: DeleteContactCommandHandler; beforeEach(() => { handler = new DeleteContactCommandHandler(mockContactRepository); }); it('should perform soft delete successfully', async () => { // Arrange const existingContact = createMockContact({ id: '550e8400-e29b-41d4-a716-446655440000' }); mockContactRepository.findById.mockResolvedValue(existingContact); mockContactRepository.softDelete.mockResolvedValue(null); const command: DeleteContactCommand = { id: '550e8400-e29b-41d4-a716-446655440000', hard: false }; // Act const result = await handler.execute(command); // Assert expect(result).toBe(true); expect(mockContactRepository.findById).toHaveBeenCalledWith('550e8400-e29b-41d4-a716-446655440000'); expect(mockContactRepository.softDelete).toHaveBeenCalledWith('550e8400-e29b-41d4-a716-446655440000'); expect(mockContactRepository.delete).not.toHaveBeenCalled(); }); it('should perform hard delete successfully', async () => { // Arrange const existingContact = createMockContact({ id: '550e8400-e29b-41d4-a716-446655440000' }); mockContactRepository.findById.mockResolvedValue(existingContact); mockContactRepository.delete.mockResolvedValue(true); const command: DeleteContactCommand = { id: '550e8400-e29b-41d4-a716-446655440000', hard: true }; // Act const result = await handler.execute(command); // Assert expect(result).toBe(true); expect(mockContactRepository.delete).toHaveBeenCalledWith('550e8400-e29b-41d4-a716-446655440000'); expect(mockContactRepository.softDelete).not.toHaveBeenCalled(); }); it('should default to soft delete when hard flag not specified', async () => { // Arrange const existingContact = createMockContact(); mockContactRepository.findById.mockResolvedValue(existingContact); mockContactRepository.softDelete.mockResolvedValue(null); const command: DeleteContactCommand = { id: '550e8400-e29b-41d4-a716-446655440000' }; // Act const result = await handler.execute(command); // Assert expect(result).toBe(true); expect(mockContactRepository.softDelete).toHaveBeenCalled(); expect(mockContactRepository.delete).not.toHaveBeenCalled(); }); it('should throw error when contact not found', async () => { // Arrange mockContactRepository.findById.mockResolvedValue(null); const command: DeleteContactCommand = { id: 'non-existent-id', hard: false }; // Act & Assert await expect(handler.execute(command)).rejects.toThrow('Contact not found'); }); it('should handle repository errors during deletion', async () => { // Arrange const existingContact = createMockContact(); mockContactRepository.findById.mockResolvedValue(existingContact); mockContactRepository.softDelete.mockRejectedValue(new Error('Database error')); const command: DeleteContactCommand = { id: 'existing-id', hard: false }; // Act & Assert await expect(handler.execute(command)).rejects.toThrow('Failed to delete contact'); }); it('should handle hard delete repository errors', async () => { // Arrange const existingContact = createMockContact(); mockContactRepository.findById.mockResolvedValue(existingContact); mockContactRepository.delete.mockRejectedValue(new Error('Database error')); const command: DeleteContactCommand = { id: 'existing-id', hard: true }; // Act & Assert await expect(handler.execute(command)).rejects.toThrow('Failed to delete contact'); }); }); });