import { IMock, It, Mock, Times } from 'typemoq' import { Logger, LoggerFactory } from '../logger' import { MessageLogger, TestCommand, TestCommand2, TestEvent, testEventHandler } from '../test' import { DefaultHandlerRegistry } from './default-handler-registry' import { HandlerAlreadyRegistered } from './error' import { HandlerDefinition, MessageBase } from './handler' describe('HandlerRegistry', () => { let logger: IMock let loggerFactory: LoggerFactory const messageLoggerMock = Mock.ofType() const handler = testEventHandler(messageLoggerMock.object) const messageType = TestEvent const genericHandler = () => undefined const handlerRegistry = new DefaultHandlerRegistry() beforeAll(() => { logger = Mock.ofType() loggerFactory = () => logger.object }) afterEach(() => handlerRegistry.reset()) describe('when registering a handler', () => { beforeEach(() => handlerRegistry.register(handler.messageType, handler.messageHandler) ) it('should register the handler', () => { const handlers = handlerRegistry.get(loggerFactory, new messageType()) expect(handlers).toHaveLength(1) }) }) describe('when registering a handler twice', () => { it('should throw a HandlerAlreadyRegistered error', () => { handlerRegistry.register(handler.messageType, handler.messageHandler) expect(() => handlerRegistry.register(handler.messageType, handler.messageHandler) ).toThrow(HandlerAlreadyRegistered) }) }) describe('when getting a handler', () => { it('should return an empty array for an unregistered handler', () => { expect(handlerRegistry.get(loggerFactory, {})).toHaveLength(0) }) it('should return a single handler for a single registration', () => { handlerRegistry.register(handler.messageType, handler.messageHandler) expect( handlerRegistry.get(loggerFactory, new messageType()) ).toHaveLength(1) }) it('should return a multiple handlers for multiple registrations', () => { handlerRegistry.register(handler.messageType, handler.messageHandler) handlerRegistry.register(messageType, () => undefined) expect( handlerRegistry.get(loggerFactory, new messageType()) ).toHaveLength(2) }) }) describe('when getting a handler for a message with no registered handlers', () => { let handlers: HandlerDefinition[] const unregisteredMessage = { $name: 'unregistered-message' } beforeEach(() => { handlers = handlerRegistry.get(loggerFactory, unregisteredMessage) }) it('should return an empty array of handlers', () => { expect(handlers).toHaveLength(0) }) it('should log an error', () => { logger.verify( l => l.error( `No handlers were registered for message`, It.isObjectWith({ messageName: unregisteredMessage.$name }) ), Times.once() ) }) describe('when the same message is handled again', () => { it('should not keep logging the error', () => { handlerRegistry.get(loggerFactory, unregisteredMessage) handlerRegistry.get(loggerFactory, unregisteredMessage) handlerRegistry.get(loggerFactory, unregisteredMessage) logger.verify( l => l.error( `No handlers were registered for message`, It.isObjectWith({ messageName: unregisteredMessage.$name }) ), Times.once() ) }) }) }) describe('when getting the list of messages registered with the handler', () => { it('should return the full set as an array', () => { handlerRegistry.register(TestEvent, genericHandler) handlerRegistry.register(TestCommand, genericHandler) const registeredMessages = handlerRegistry.getMessageNames() ;[TestEvent, TestCommand].forEach(messageType => { expect(registeredMessages).toContain(new messageType().$name) }) }) }) describe('when getting a message constructor', () => { describe('for a registered message', () => { it('should return a message constructor', () => { handlerRegistry.register(TestEvent, genericHandler) const ctor = handlerRegistry.getMessageConstructor(TestEvent.NAME) expect(ctor).toEqual(TestEvent) }) }) describe('for an unregistered message', () => { it('should return undefined', () => { const ctor = handlerRegistry.getMessageConstructor('abc') expect(ctor).toBeUndefined() }) }) }) describe('when registering a message handler using a custom resolver', () => { class CustomHandler { async handle(_: TestCommand2): Promise { // ... } } beforeAll(async () => { handlerRegistry.registerCustom(CustomHandler, { resolveWith: message => message.$name === TestCommand2.NAME, topicIdentifier: 'arn:aws:sns:us-east-1:000000000000:s3-object-created' }) }) describe('and then getting a handler for the message type', () => { it('should resolve using the custom handler', () => { const resolvedHandlers = handlerRegistry.get( loggerFactory, new TestCommand2() ) expect(resolvedHandlers).toHaveLength(1) expect(resolvedHandlers[0]).toEqual(CustomHandler) }) }) }) })