403 lines
13 KiB
TypeScript
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');
|
|
});
|
|
});
|
|
});
|