Files
SerpentRace/SerpentRace_Backend/tests/Application/Contact/commands/ContactCommandHandlers.comprehensive.test.ts
T

403 lines
13 KiB
TypeScript

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