Files
SerpentRace/SerpentRace_Backend/tests/Application/Organization/commands/OrganizationCommandHandlers.comprehensive.test.ts
T

334 lines
11 KiB
TypeScript

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<typeof createMockOrganizationRepository>;
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');
});
});
});