Skip to content
Snippets Groups Projects
Commit 77721fc5 authored by Peter Hooper's avatar Peter Hooper
Browse files

End of day

parent 57af8edc
No related branches found
No related tags found
No related merge requests found
import { EventBus } from './event-bus';
describe('Event Bus class', () => {
it('EventBus is abstract', () => {
const create = (T): EventBus => new T(['test'], 'test');
const x = create(EventBus);
expect(x.eventsToHandle).toStrictEqual(['test']);
expect(x.serviceName).toStrictEqual('test');
expect(() => x.destroy()).toThrow('x.destroy is not a function');
});
it('EventBus can be extended', () => {
class EB extends EventBus {
called = false;
destroy(): Promise<void> {
this.called = true;
return Promise.resolve();
}
}
const x = new EB(['test'], 'test');
expect(x.eventsToHandle).toStrictEqual(['test']);
expect(x.serviceName).toStrictEqual('test');
expect(x.destroy()).resolves.toBe(undefined);
expect(x.called).toBe(true);
});
});
import { eventFactory, eventAbstractFactory } from './event-factory';
import { Event, Payload } from './types';
describe('Event Creation', () => {
describe('eventFactory', () => {
it('creates an Event', () => {
const payload = new Payload();
const event = eventFactory('type', payload);
expect(event.id).toHaveLength(36);
expect(event.eventType).toBe('type');
expect(event.created instanceof Date).toBe(true);
expect(event.payload).toBe(payload);
});
it('fails to create an Event without a payload of type Payload', () => {
expect(() => eventFactory('type', {})).toThrow('argument payload is not of type Payload!');
});
});
describe('eventAbstractFactory', () => {
class Party extends Payload {
beer: boolean;
address: string;
}
class EventParty extends Event {}
class GardenParty extends Payload {
wine: boolean;
}
it('creates an Event of correct type', () => {
const payload = new Party();
payload.beer = true;
payload.address = 'my place';
const event: EventParty = eventAbstractFactory('party', Party, payload);
expect(event.id).toHaveLength(36);
expect(event.eventType).toBe('party');
expect(event.created instanceof Date).toBe(true);
expect(event.payload).toHaveProperty('beer');
expect(event.payload['beer']).toBe(true);
expect(event.payload).toHaveProperty('address');
expect(event.payload['address']).toBe('my place');
});
it('rejects arbitrary payload type', () => {
const payload = { address: 'my place' };
const create = (p: object): EventParty => eventAbstractFactory('party', Party, p);
expect(() => create(payload)).toThrow('expected payload of type Party got Object');
});
it('rejects incorrect payload type', () => {
const payload = new GardenParty();
payload.wine = true;
const create = (p: GardenParty): EventParty => eventAbstractFactory('party', Party, p);
expect(() => create(payload)).toThrow('expected payload of type Party got GardenParty');
});
});
});
import { Event } from './types'; import { Event, Payload } from './types';
import uuid = require('uuid'); import uuid = require('uuid');
export const eventFactory = <P extends object>(eventType: string, payload: P): Event<P> => { export const eventFactory = (eventType: string, payload: Payload): Event => {
if (!(payload instanceof Payload)) {
throw new TypeError('argument payload is not of type Payload!');
}
return { return {
eventType, eventType,
id: uuid.v4(), id: uuid.v4(),
...@@ -9,3 +12,11 @@ export const eventFactory = <P extends object>(eventType: string, payload: P): E ...@@ -9,3 +12,11 @@ export const eventFactory = <P extends object>(eventType: string, payload: P): E
payload, payload,
}; };
}; };
export const eventAbstractFactory = (typeName: string, payloadClass, p: object): Event => {
if (payloadClass.name !== p.constructor.name) {
throw new TypeError(`expected payload of type ${payloadClass.name} got ${p.constructor.name}`);
}
const event = eventFactory(typeName, p);
return event;
};
// Abstract Message Queue - types and interfaces // Abstract Message Queue - types and interfaces
/**
* TODO: Once agreed, add these to DefinitelyTyped so they can be shared.
*/
export type EventType = string; export type EventType = string;
export interface Event<T extends object> { export class Payload {}
export class Event {
readonly eventType: EventType; readonly eventType: EventType;
readonly id: string; // Generated when the event is emitted readonly id: string; // Generated when the event is emitted
readonly created: Date; readonly created: Date;
readonly payload: T; // The actual data the event is carrying.
// version: has been removed - so we can remain weakly typed // version: has been removed - so we can remain weakly typed
// context: has also been removed - if you need information about the origin // context: has also been removed - if you need information about the origin
// source of the event then put it in the payload. // source of the event then put it in the payload.
constructor(readonly payload: Payload) {}
} }
export interface EventPublisher { export interface EventPublisher {
// Promise<boolean> should this become void | exception? we only need to know if something went wrong // Promise<boolean> should this become void | exception? we only need to know if something went wrong
publish<T extends object>(event: Event<T>): Promise<boolean>; publish(event: Event): Promise<boolean>;
} }
export interface EventSubscriber { export interface EventSubscriber {
// handler: returns whether or not we should ack the message // handler: returns whether or not we should ack the message
subscribe<T extends object>(eventType: string, handler: (event: Event<T>) => Promise<boolean>): void; subscribe(eventType: string, handler: (event: Event) => Promise<boolean>): void;
} }
// This isn't generic enough // This isn't generic enough
......
import { MockEventBus } from '.'; import { MockEventBus } from './index';
import { Event, eventFactory } from '../event-bus'; import { Event, Payload } from '../event-bus/types';
import { eventFactory } from '../event-bus/event-factory';
interface TestEventPayload1 {
x: number;
y: number;
}
interface TestEventPayload2 {
a: number;
b: number;
}
type MockEventType1 = Event<TestEventPayload1>;
type MockEventType2 = Event<TestEventPayload2>;
describe('mock message queue', () => { describe('mock message queue', () => {
// describe('object lifetime', () => { // describe('object lifetime', () => {
...@@ -26,13 +14,14 @@ describe('mock message queue', () => { ...@@ -26,13 +14,14 @@ describe('mock message queue', () => {
const eventType = 'libero:mock:test'; const eventType = 'libero:mock:test';
const mockHandler = jest.fn(async () => true); const mockHandler = jest.fn(async () => true);
const mockEventBus = await new MockEventBus([eventType], 'message-bus-test'); const mockEventBus = new MockEventBus([eventType], 'message-bus-test');
mockEventBus.subscribe<MockEventType1>(eventType, mockHandler); mockEventBus.subscribe(eventType, mockHandler);
const payload = new Payload();
const event = eventFactory<TestEventPayload1>(eventType, { x: 10, y: 20 }); const event = eventFactory(eventType, payload);
mockEventBus.publish<TestEventPayload1>(event); mockEventBus.publish(event);
expect(mockHandler).toBeCalled(); expect(mockHandler).toBeCalled();
expect(mockHandler.mock.calls).toEqual([[event]]); expect(mockHandler.mock.calls).toEqual([[event]]);
...@@ -42,14 +31,14 @@ describe('mock message queue', () => { ...@@ -42,14 +31,14 @@ describe('mock message queue', () => {
const eventType1 = 'libero:mock:test1'; const eventType1 = 'libero:mock:test1';
const eventType2 = 'libero:mock:test2'; const eventType2 = 'libero:mock:test2';
const event1: MockEventType1 = { const event1: Event = {
eventType: eventType1, eventType: eventType1,
id: 'some-testing-event1-id', id: 'some-testing-event1-id',
created: new Date(), created: new Date(),
payload: { x: 10, y: 20 }, payload: { x: 10, y: 20 },
}; };
const event2: MockEventType2 = { const event2: Event = {
eventType: eventType2, eventType: eventType2,
id: 'some-testing-event2-id', id: 'some-testing-event2-id',
created: new Date(), created: new Date(),
...@@ -61,14 +50,14 @@ describe('mock message queue', () => { ...@@ -61,14 +50,14 @@ describe('mock message queue', () => {
let receivedEvent1: object = {}; let receivedEvent1: object = {};
let receivedEvent2: object = {}; let receivedEvent2: object = {};
const mockHandler1 = async (event: Event<TestEventPayload1>): Promise<boolean> => { const mockHandler1 = async (event: Event): Promise<boolean> => {
handler1 += 1; handler1 += 1;
receivedEvent1 = event; receivedEvent1 = event;
return Promise.resolve(true); return Promise.resolve(true);
}; };
const mockHandler2 = async (event: Event<TestEventPayload2>): Promise<boolean> => { const mockHandler2 = async (event: Event): Promise<boolean> => {
handler2 += 1; handler2 += 1;
receivedEvent2 = event; receivedEvent2 = event;
return Promise.resolve(true); return Promise.resolve(true);
...@@ -76,10 +65,10 @@ describe('mock message queue', () => { ...@@ -76,10 +65,10 @@ describe('mock message queue', () => {
const mockEventBus = new MockEventBus([eventType1, eventType2], 'message-bus-test'); const mockEventBus = new MockEventBus([eventType1, eventType2], 'message-bus-test');
mockEventBus.subscribe<TestEventPayload1>(eventType1, mockHandler1); mockEventBus.subscribe(eventType1, mockHandler1);
mockEventBus.subscribe<TestEventPayload2>(eventType2, mockHandler2); mockEventBus.subscribe(eventType2, mockHandler2);
mockEventBus.publish<TestEventPayload2>(event2); mockEventBus.publish(event2);
expect(handler1).toBe(0); expect(handler1).toBe(0);
expect(handler2).toBe(1); expect(handler2).toBe(1);
...@@ -88,7 +77,7 @@ describe('mock message queue', () => { ...@@ -88,7 +77,7 @@ describe('mock message queue', () => {
receivedEvent1 = {}; receivedEvent1 = {};
receivedEvent2 = {}; receivedEvent2 = {};
mockEventBus.publish<TestEventPayload1>(event1); mockEventBus.publish(event1);
expect(handler1).toBe(1); expect(handler1).toBe(1);
expect(handler2).toBe(1); expect(handler2).toBe(1);
......
import { EventBus, Event, EventPublisher, EventSubscriber } from '../event-bus'; import { EventBus, Event, EventPublisher, EventSubscriber } from '../event-bus';
export type AnyEvent = Event<object>; export type AnyEvent = Event;
export type AnyHandler = (ev: AnyEvent) => Promise<boolean>; export type AnyHandler = (ev: AnyEvent) => Promise<boolean>;
/** /**
...@@ -10,7 +10,7 @@ export type AnyHandler = (ev: AnyEvent) => Promise<boolean>; ...@@ -10,7 +10,7 @@ export type AnyHandler = (ev: AnyEvent) => Promise<boolean>;
export class MockEventBus extends EventBus implements EventPublisher, EventSubscriber { export class MockEventBus extends EventBus implements EventPublisher, EventSubscriber {
private queues: Map<string, AnyHandler> = new Map(); private queues: Map<string, AnyHandler> = new Map();
public async publish<T extends object>(event: Event<T>): Promise<boolean> { public async publish(event: Event): Promise<boolean> {
const fn = this.queues.get(`${event.eventType}`); const fn = this.queues.get(`${event.eventType}`);
if (fn) { if (fn) {
if (this.eventsToHandle.includes(event.eventType)) { if (this.eventsToHandle.includes(event.eventType)) {
...@@ -20,10 +20,7 @@ export class MockEventBus extends EventBus implements EventPublisher, EventSubsc ...@@ -20,10 +20,7 @@ export class MockEventBus extends EventBus implements EventPublisher, EventSubsc
return Promise.resolve(false); return Promise.resolve(false);
} }
public async subscribe<T extends object>( public async subscribe(eventType: string, handler: (event: Event) => Promise<boolean>): Promise<void> {
eventType: string,
handler: (event: Event<T>) => Promise<boolean>,
): Promise<void> {
if (!this.serviceName) { if (!this.serviceName) {
Promise.reject(`Service name not set!`); Promise.reject(`Service name not set!`);
} }
......
...@@ -134,7 +134,7 @@ describe('AMQP connector', () => { ...@@ -134,7 +134,7 @@ describe('AMQP connector', () => {
// we need to wait for connection to be stored before we can publish // we need to wait for connection to be stored before we can publish
await flushPromises(); await flushPromises();
await connector.publish(event as Event<{}>); await connector.publish(event as Event);
await flushPromises(); await flushPromises();
expect(mockChannel.publish).toHaveBeenCalledTimes(1); expect(mockChannel.publish).toHaveBeenCalledTimes(1);
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment