import { CreateOrganizationCommandHandler } from '../../../../src/Application/Organization/commands/CreateOrganizationCommandHandler'; import { UpdateOrganizationCommandHandler } from '../../../../src/Application/Organization/commands/UpdateOrganizationCommandHandler'; import { DeleteOrganizationCommandHandler } from '../../../../src/Application/Organization/commands/DeleteOrganizationCommandHandler'; import { CreateOrganizationCommand } from '../../../../src/Application/Organization/commands/CreateOrganizationCommand'; import { UpdateOrganizationCommand } from '../../../../src/Application/Organization/commands/UpdateOrganizationCommand'; import { DeleteOrganizationCommand } from '../../../../src/Application/Organization/commands/DeleteOrganizationCommand'; import { OrganizationState } from '../../../../src/Domain/Organization/OrganizationAggregate'; import { createMockOrganizationRepository, createMockOrganization } from '../../../testUtils'; describe('Organization Command Handlers - Comprehensive', () => { let mockOrganizationRepository: ReturnType; beforeEach(() => { mockOrganizationRepository = createMockOrganizationRepository(); }); describe('CreateOrganizationCommandHandler', () => { let handler: CreateOrganizationCommandHandler; beforeEach(() => { handler = new CreateOrganizationCommandHandler(mockOrganizationRepository); }); it('should create organization successfully', async () => { // Arrange const mockOrgData = createMockOrganization({ id: '550e8400-e29b-41d4-a716-446655440000', name: 'Test Organization', contactfname: 'John', contactlname: 'Doe', contactphone: '+1234567890', contactemail: 'john@testorg.com', url: null, state: OrganizationState.REGISTERED }); mockOrganizationRepository.create.mockResolvedValue(mockOrgData); const command: CreateOrganizationCommand = { name: 'Test Organization', contactfname: 'John', contactlname: 'Doe', contactemail: 'john@testorg.com', contactphone: '+1234567890' }; // Act const result = await handler.execute(command); // Assert - Returns ShortOrganizationDto expect(result).toEqual({ id: '550e8400-e29b-41d4-a716-446655440000', name: 'Test Organization', state: 0, userinorg: 0, maxOrganizationalDecks: 10 }); expect(mockOrganizationRepository.create).toHaveBeenCalledWith( expect.objectContaining({ name: 'Test Organization', contactfname: 'John', contactlname: 'Doe', contactemail: 'john@testorg.com', contactphone: '+1234567890', state: OrganizationState.REGISTERED }) ); }); it('should create organization with optional URL', async () => { // Arrange const mockOrgData = createMockOrganization({ id: '550e8400-e29b-41d4-a716-446655440001', name: 'Org with URL', contactfname: 'Jane', contactlname: 'Smith', contactphone: '+1987654321', contactemail: 'jane@orgwithurl.com', url: 'https://orgwithurl.com', state: OrganizationState.REGISTERED }); mockOrganizationRepository.create.mockResolvedValue(mockOrgData); const command: CreateOrganizationCommand = { name: 'Org with URL', contactfname: 'Jane', contactlname: 'Smith', contactemail: 'jane@orgwithurl.com', contactphone: '+1987654321', url: 'https://orgwithurl.com' }; // Act const result = await handler.execute(command); // Assert expect(result).toEqual({ id: '550e8400-e29b-41d4-a716-446655440001', name: 'Org with URL', state: 0, userinorg: 0, maxOrganizationalDecks: 10 }); }); it('should handle duplicate organization name error', async () => { // Arrange const command: CreateOrganizationCommand = { name: 'Duplicate Org', contactfname: 'John', contactlname: 'Doe', contactemail: 'john@duplicate.com', contactphone: '+1234567890' }; const duplicateError = new Error('duplicate key value violates unique constraint "organization_name_unique"'); mockOrganizationRepository.create.mockRejectedValue(duplicateError); // Act & Assert await expect(handler.execute(command)).rejects.toThrow('Organization with this name or contact email already exists'); }); it('should handle generic database errors', async () => { // Arrange const command: CreateOrganizationCommand = { name: 'Error Org', contactfname: 'John', contactlname: 'Doe', contactemail: 'john@error.com', contactphone: '+1234567890' }; mockOrganizationRepository.create.mockRejectedValue(new Error('Database connection failed')); // Act & Assert await expect(handler.execute(command)).rejects.toThrow('Failed to create organization'); }); it('should handle non-Error exceptions', async () => { // Arrange const command: CreateOrganizationCommand = { name: 'Non-Error Exception Org', contactfname: 'John', contactlname: 'Doe', contactemail: 'john@exception.com', contactphone: '+1234567890' }; mockOrganizationRepository.create.mockRejectedValue('String error'); // Act & Assert await expect(handler.execute(command)).rejects.toThrow('Failed to create organization'); }); }); describe('UpdateOrganizationCommandHandler', () => { let handler: UpdateOrganizationCommandHandler; beforeEach(() => { handler = new UpdateOrganizationCommandHandler(mockOrganizationRepository); }); it('should update organization successfully', async () => { // Arrange const updatedOrgData = createMockOrganization({ id: '550e8400-e29b-41d4-a716-446655440000', name: 'Updated Organization', contactemail: 'john@updated.com', url: 'https://updated.com', state: OrganizationState.ACTIVE }); mockOrganizationRepository.update.mockResolvedValue(updatedOrgData); const command: UpdateOrganizationCommand = { id: '550e8400-e29b-41d4-a716-446655440000', name: 'Updated Organization', contactemail: 'john@updated.com', url: 'https://updated.com' }; // Act const result = await handler.execute(command); // Assert - Returns ShortOrganizationDto expect(result).toEqual({ id: '550e8400-e29b-41d4-a716-446655440000', name: 'Updated Organization', state: 1, userinorg: 0, maxOrganizationalDecks: 10 }); expect(mockOrganizationRepository.update).toHaveBeenCalledWith( '550e8400-e29b-41d4-a716-446655440000', command ); }); it('should return null when organization not found', async () => { // Arrange mockOrganizationRepository.update.mockResolvedValue(null); const command: UpdateOrganizationCommand = { id: 'non-existent-id', name: 'Non-existent Organization' }; // Act const result = await handler.execute(command); // Assert expect(result).toBeNull(); expect(mockOrganizationRepository.update).toHaveBeenCalledWith('non-existent-id', command); }); it('should update organization with partial data', async () => { // Arrange const partialUpdatedOrgData = createMockOrganization({ id: '550e8400-e29b-41d4-a716-446655440000', name: 'Original Name', contactemail: 'john@newmail.com', state: OrganizationState.ACTIVE }); mockOrganizationRepository.update.mockResolvedValue(partialUpdatedOrgData); const command: UpdateOrganizationCommand = { id: '550e8400-e29b-41d4-a716-446655440000', contactemail: 'john@newmail.com' }; // Act const result = await handler.execute(command); // Assert expect(result).toEqual({ id: '550e8400-e29b-41d4-a716-446655440000', name: 'Original Name', state: 1, userinorg: 0, maxOrganizationalDecks: 10 }); }); }); describe('DeleteOrganizationCommandHandler', () => { let handler: DeleteOrganizationCommandHandler; beforeEach(() => { handler = new DeleteOrganizationCommandHandler(mockOrganizationRepository); }); it('should perform soft delete successfully', async () => { // Arrange mockOrganizationRepository.softDelete.mockResolvedValue(null); const command: DeleteOrganizationCommand = { id: '550e8400-e29b-41d4-a716-446655440000', soft: true }; // Act const result = await handler.execute(command); // Assert expect(result).toBe(true); expect(mockOrganizationRepository.softDelete).toHaveBeenCalledWith('550e8400-e29b-41d4-a716-446655440000'); expect(mockOrganizationRepository.delete).not.toHaveBeenCalled(); }); it('should perform hard delete successfully', async () => { // Arrange mockOrganizationRepository.delete.mockResolvedValue(true); const command: DeleteOrganizationCommand = { id: '550e8400-e29b-41d4-a716-446655440000', soft: false }; // Act const result = await handler.execute(command); // Assert expect(result).toBe(true); expect(mockOrganizationRepository.delete).toHaveBeenCalledWith('550e8400-e29b-41d4-a716-446655440000'); expect(mockOrganizationRepository.softDelete).not.toHaveBeenCalled(); }); it('should default to hard delete when soft flag not specified', async () => { // Arrange mockOrganizationRepository.delete.mockResolvedValue(true); const command: DeleteOrganizationCommand = { id: '550e8400-e29b-41d4-a716-446655440000' }; // Act const result = await handler.execute(command); // Assert expect(result).toBe(true); expect(mockOrganizationRepository.delete).toHaveBeenCalledWith('550e8400-e29b-41d4-a716-446655440000'); expect(mockOrganizationRepository.softDelete).not.toHaveBeenCalled(); }); it('should handle soft delete with repository error gracefully', async () => { // Arrange mockOrganizationRepository.softDelete.mockRejectedValue(new Error('Database error')); const command: DeleteOrganizationCommand = { id: '550e8400-e29b-41d4-a716-446655440000', soft: true }; // Act & Assert - Handler doesn't catch errors, they bubble up await expect(handler.execute(command)).rejects.toThrow('Database error'); }); it('should handle hard delete with repository error gracefully', async () => { // Arrange mockOrganizationRepository.delete.mockRejectedValue(new Error('Database error')); const command: DeleteOrganizationCommand = { id: '550e8400-e29b-41d4-a716-446655440000', soft: false }; // Act & Assert - Handler doesn't catch errors, they bubble up await expect(handler.execute(command)).rejects.toThrow('Database error'); }); }); });