Move src/ → apps/server/ and packages/web/ → apps/web/ to adopt standard monorepo conventions (apps/ for runnable apps, packages/ for reusable libraries). Update all config files, shared package imports, test fixtures, and documentation to reflect new paths. Key fixes: - Update workspace config to ["apps/*", "packages/*"] - Update tsconfig.json rootDir/include for apps/server/ - Add apps/web/** to vitest exclude list - Update drizzle.config.ts schema path - Fix ensure-schema.ts migration path detection (3 levels up in dev, 2 levels up in dist) - Fix tests/integration/cli-server.test.ts import paths - Update packages/shared imports to apps/server/ paths - Update all docs/ files with new paths
202 lines
5.4 KiB
TypeScript
202 lines
5.4 KiB
TypeScript
/**
|
|
* 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<ProcessSpawnedEvent>('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<ServerStartedEvent>('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);
|
|
});
|
|
});
|