/** * EventBus Tests * * Tests for the EventEmitterBus adapter implementation. * Verifies the event bus pattern works correctly with typed events. */ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { EventEmitterBus, createEventBus } from './index.js'; import type { DomainEvent, ProcessSpawnedEvent, ServerStartedEvent, } from './types.js'; describe('EventEmitterBus', () => { let bus: EventEmitterBus; beforeEach(() => { bus = new EventEmitterBus(); }); describe('emit/on pattern', () => { it('should deliver events to subscribed handlers', () => { const handler = vi.fn(); const event: DomainEvent = { type: 'test:event', timestamp: new Date(), payload: { data: 'test' }, }; bus.on('test:event', handler); bus.emit(event); expect(handler).toHaveBeenCalledOnce(); expect(handler).toHaveBeenCalledWith(event); }); it('should not deliver events to handlers of different types', () => { const handler = vi.fn(); const event: DomainEvent = { type: 'test:event', timestamp: new Date(), payload: { data: 'test' }, }; bus.on('other:event', handler); bus.emit(event); expect(handler).not.toHaveBeenCalled(); }); }); describe('once', () => { it('should fire handler only once', () => { const handler = vi.fn(); const event: DomainEvent = { type: 'test:event', timestamp: new Date(), payload: { data: 'test' }, }; bus.once('test:event', handler); bus.emit(event); bus.emit(event); bus.emit(event); expect(handler).toHaveBeenCalledOnce(); }); }); describe('off', () => { it('should remove handler from event subscription', () => { const handler = vi.fn(); const event: DomainEvent = { type: 'test:event', timestamp: new Date(), payload: { data: 'test' }, }; bus.on('test:event', handler); bus.emit(event); expect(handler).toHaveBeenCalledOnce(); bus.off('test:event', handler); bus.emit(event); expect(handler).toHaveBeenCalledOnce(); // Still only once }); }); describe('multiple handlers', () => { it('should deliver events to all handlers for the same event type', () => { const handler1 = vi.fn(); const handler2 = vi.fn(); const handler3 = vi.fn(); const event: DomainEvent = { type: 'test:event', timestamp: new Date(), payload: { data: 'test' }, }; bus.on('test:event', handler1); bus.on('test:event', handler2); bus.on('test:event', handler3); bus.emit(event); expect(handler1).toHaveBeenCalledOnce(); expect(handler2).toHaveBeenCalledOnce(); expect(handler3).toHaveBeenCalledOnce(); }); }); describe('typed events', () => { it('should work with ProcessSpawnedEvent', () => { const handler = vi.fn(); const event: ProcessSpawnedEvent = { type: 'process:spawned', timestamp: new Date(), payload: { processId: 'proc-1', pid: 12345, command: 'node server.js', }, }; bus.on('process:spawned', handler); bus.emit(event); expect(handler).toHaveBeenCalledWith(event); const receivedEvent = handler.mock.calls[0][0] as ProcessSpawnedEvent; expect(receivedEvent.payload.processId).toBe('proc-1'); expect(receivedEvent.payload.pid).toBe(12345); expect(receivedEvent.payload.command).toBe('node server.js'); }); it('should work with ServerStartedEvent', () => { const handler = vi.fn(); const event: ServerStartedEvent = { type: 'server:started', timestamp: new Date(), payload: { port: 3847, host: '127.0.0.1', pid: 54321, }, }; bus.on('server:started', handler); bus.emit(event); expect(handler).toHaveBeenCalledWith(event); const receivedEvent = handler.mock.calls[0][0] as ServerStartedEvent; expect(receivedEvent.payload.port).toBe(3847); expect(receivedEvent.payload.host).toBe('127.0.0.1'); expect(receivedEvent.payload.pid).toBe(54321); }); }); describe('timestamp', () => { it('should preserve event timestamp through emit/on cycle', () => { const handler = vi.fn(); const timestamp = new Date('2026-01-30T12:00:00Z'); const event: DomainEvent = { type: 'test:event', timestamp, payload: { data: 'test' }, }; bus.on('test:event', handler); bus.emit(event); const receivedEvent = handler.mock.calls[0][0] as DomainEvent; expect(receivedEvent.timestamp).toBe(timestamp); expect(receivedEvent.timestamp.toISOString()).toBe( '2026-01-30T12:00:00.000Z' ); }); }); }); describe('createEventBus', () => { it('should create an EventEmitterBus instance', () => { const bus = createEventBus(); expect(bus).toBeInstanceOf(EventEmitterBus); }); it('should return a working EventBus', () => { const bus = createEventBus(); const handler = vi.fn(); const event: DomainEvent = { type: 'test:event', timestamp: new Date(), payload: { data: 'test' }, }; bus.on('test:event', handler); bus.emit(event); expect(handler).toHaveBeenCalledWith(event); }); });