@sailboat-computer/event-bus
Version:
Standardized event bus for sailboat computer v3 with resilience features and offline capabilities
318 lines (257 loc) • 9.66 kB
text/typescript
/**
* Dead letter queue tests
*/
import { DeadLetterQueueManager } from '../../src/dead-letter-queue';
import { EventPriority, EventCategory, DeadLetterEvent } from '../../src/types';
// Extended type for DeadLetterEvent with id property added by getEvents()
interface DeadLetterEventWithId extends DeadLetterEvent {
id: string;
}
describe('DeadLetterQueueManager', () => {
let deadLetterQueue: DeadLetterQueueManager;
beforeEach(() => {
// Create a new dead letter queue manager for each test
deadLetterQueue = new DeadLetterQueueManager(10, 3);
});
describe('initialization', () => {
it('should initialize with default values', () => {
const defaultQueue = new DeadLetterQueueManager();
expect(defaultQueue).toBeDefined();
});
it('should initialize with custom values', () => {
const customQueue = new DeadLetterQueueManager(20, 5);
expect(customQueue).toBeDefined();
});
});
describe('addEvent', () => {
it('should add an event to the queue', () => {
const event = {
id: 'test-id',
type: 'test.event',
timestamp: new Date(),
source: 'test-service',
version: '1.0',
data: { message: 'Hello, world!' },
metadata: {
priority: EventPriority.NORMAL,
category: EventCategory.DATA
}
};
const error = new Error('Test error');
const attempts = 3;
const eventId = deadLetterQueue.addEvent(event, error, attempts);
expect(eventId).toBeDefined();
expect(deadLetterQueue.getSize()).toBe(1);
});
it('should enforce the maximum queue size', () => {
// Create a queue with max size 3
const smallQueue = new DeadLetterQueueManager(3, 3);
// Add 4 events
for (let i = 0; i < 4; i++) {
const event = {
id: `test-id-${i}`,
type: 'test.event',
timestamp: new Date(),
source: 'test-service',
version: '1.0',
data: { message: `Event ${i}` },
metadata: {
priority: EventPriority.NORMAL,
category: EventCategory.DATA
}
};
const error = new Error(`Test error ${i}`);
const attempts = 3;
smallQueue.addEvent(event, error, attempts);
}
// Check that the queue size is still 3
expect(smallQueue.getSize()).toBe(3);
// Check that the oldest event was removed
const events = smallQueue.getEvents();
const ids = events.map(e => e.originalEventId);
expect(ids).not.toContain('test-id-0');
expect(ids).toContain('test-id-1');
expect(ids).toContain('test-id-2');
expect(ids).toContain('test-id-3');
});
});
describe('getEvent', () => {
it('should get an event by ID', () => {
const event = {
id: 'test-id',
type: 'test.event',
timestamp: new Date(),
source: 'test-service',
version: '1.0',
data: { message: 'Hello, world!' },
metadata: {
priority: EventPriority.NORMAL,
category: EventCategory.DATA
}
};
const error = new Error('Test error');
const attempts = 3;
const eventId = deadLetterQueue.addEvent(event, error, attempts);
const retrievedEvent = deadLetterQueue.getEvent(eventId);
expect(retrievedEvent).toBeDefined();
expect(retrievedEvent?.originalEventId).toBe('test-id');
expect(retrievedEvent?.eventType).toBe('test.event');
expect(retrievedEvent?.data).toEqual({ message: 'Hello, world!' });
expect(retrievedEvent?.attempts).toBe(3);
expect(retrievedEvent?.error).toBeDefined();
expect(retrievedEvent?.error.message).toBe('Test error');
});
it('should return undefined for non-existent event ID', () => {
const retrievedEvent = deadLetterQueue.getEvent('non-existent-id');
expect(retrievedEvent).toBeNull();
});
});
describe('getEvents', () => {
beforeEach(() => {
// Add some events of different types
for (let i = 0; i < 5; i++) {
const eventType = i % 2 === 0 ? 'test.event.even' : 'test.event.odd';
const event = {
id: `test-id-${i}`,
type: eventType,
timestamp: new Date(),
source: 'test-service',
version: '1.0',
data: { message: `Event ${i}` },
metadata: {
priority: EventPriority.NORMAL,
category: EventCategory.DATA
}
};
const error = new Error(`Test error ${i}`);
const attempts = 3;
deadLetterQueue.addEvent(event, error, attempts);
}
});
it('should get all events', () => {
const events = deadLetterQueue.getEvents();
expect(events).toBeDefined();
expect(events.length).toBe(5);
});
it('should get events by type', () => {
const evenEvents = deadLetterQueue.getEvents('test.event.even');
const oddEvents = deadLetterQueue.getEvents('test.event.odd');
expect(evenEvents).toBeDefined();
expect(evenEvents.length).toBe(3); // 0, 2, 4
expect(oddEvents).toBeDefined();
expect(oddEvents.length).toBe(2); // 1, 3
});
it('should limit the number of events returned', () => {
const limitedEvents = deadLetterQueue.getEvents(undefined, 3);
expect(limitedEvents).toBeDefined();
expect(limitedEvents.length).toBe(3);
});
it('should limit the number of events by type', () => {
const limitedEvenEvents = deadLetterQueue.getEvents('test.event.even', 2);
expect(limitedEvenEvents).toBeDefined();
expect(limitedEvenEvents.length).toBe(2);
});
});
describe('removeEvent', () => {
let eventId: string;
beforeEach(() => {
// Add an event
const event = {
id: 'test-id',
type: 'test.event',
timestamp: new Date(),
source: 'test-service',
version: '1.0',
data: { message: 'Hello, world!' },
metadata: {
priority: EventPriority.NORMAL,
category: EventCategory.DATA
}
};
const error = new Error('Test error');
const attempts = 3;
eventId = deadLetterQueue.addEvent(event, error, attempts);
});
it('should remove an event by ID', () => {
const removed = deadLetterQueue.removeEvent(eventId);
expect(removed).toBe(true);
expect(deadLetterQueue.getSize()).toBe(0);
expect(deadLetterQueue.getEvent(eventId)).toBeNull();
});
it('should return false for non-existent event ID', () => {
const removed = deadLetterQueue.removeEvent('non-existent-id');
expect(removed).toBe(false);
expect(deadLetterQueue.getSize()).toBe(1);
});
});
describe('clearEvents', () => {
beforeEach(() => {
// Add some events of different types
for (let i = 0; i < 5; i++) {
const eventType = i % 2 === 0 ? 'test.event.even' : 'test.event.odd';
const event = {
id: `test-id-${i}`,
type: eventType,
timestamp: new Date(),
source: 'test-service',
version: '1.0',
data: { message: `Event ${i}` },
metadata: {
priority: EventPriority.NORMAL,
category: EventCategory.DATA
}
};
const error = new Error(`Test error ${i}`);
const attempts = 3;
deadLetterQueue.addEvent(event, error, attempts);
}
});
it('should clear all events', () => {
const clearedCount = deadLetterQueue.clearEvents();
expect(clearedCount).toBe(5);
expect(deadLetterQueue.getSize()).toBe(0);
});
it('should clear events by type', () => {
const clearedCount = deadLetterQueue.clearEvents('test.event.even');
expect(clearedCount).toBe(3); // 0, 2, 4
expect(deadLetterQueue.getSize()).toBe(2); // 1, 3 remain
const remainingEvents = deadLetterQueue.getEvents();
expect(remainingEvents.length).toBe(2);
expect(remainingEvents[0]?.eventType).toBe('test.event.odd');
expect(remainingEvents[1]?.eventType).toBe('test.event.odd');
});
});
describe('getSize', () => {
it('should return the correct queue size', () => {
expect(deadLetterQueue.getSize()).toBe(0);
// Add some events
for (let i = 0; i < 3; i++) {
const event = {
id: `test-id-${i}`,
type: 'test.event',
timestamp: new Date(),
source: 'test-service',
version: '1.0',
data: { message: `Event ${i}` },
metadata: {
priority: EventPriority.NORMAL,
category: EventCategory.DATA
}
};
const error = new Error(`Test error ${i}`);
const attempts = 3;
deadLetterQueue.addEvent(event, error, attempts);
}
expect(deadLetterQueue.getSize()).toBe(3);
// Remove an event - getEvents() adds the id property to the returned events
const events = deadLetterQueue.getEvents() as DeadLetterEventWithId[];
if (events.length > 0 && events[0]) {
deadLetterQueue.removeEvent(events[0].id);
}
expect(deadLetterQueue.getSize()).toBe(2);
// Clear all events
deadLetterQueue.clearEvents();
expect(deadLetterQueue.getSize()).toBe(0);
});
});
});