public void EmptyBodyThrowsException(string destQueue, string body)
        {
            // Arrange
            var contextMock = new Mock <IBusContext <IConnection> >();
            var senderMock  = new Mock <ICommandSender>();

            contextMock.Setup(e => e.CreateCommandSender())
            .Returns(senderMock.Object);

            senderMock.Setup(e => e.SendCommandAsync(It.IsAny <CommandMessage>()))
            .Returns(Task.Run(() => new CommandMessage {
                Body = null
            }));

            var sender = new CommandPublisher(contextMock.Object);

            var command = new TestCommand(destQueue);

            // Act
            async Task <TestCommand> Act() => await sender.PublishAsync <TestCommand>(command);

            // Assert
            DestinationQueueException exception = Assert.ThrowsExceptionAsync <DestinationQueueException>(Act).Result;

            Assert.AreEqual($"ArgumentNullException was thrown, most likely because the destination queue {destQueue} replied with an empty body.", exception.Message);
        }
        public void PublishAsyncCallsPublishAsyncOnSender(string destQueue, string body)
        {
            // Arrange
            var contextMock = new Mock <IBusContext <IConnection> >();
            var senderMock  = new Mock <ICommandSender>();

            contextMock.Setup(e => e.CreateCommandSender())
            .Returns(senderMock.Object);

            CommandMessage message = new CommandMessage();

            senderMock.Setup(e => e.SendCommandAsync(It.IsAny <CommandMessage>()))
            .Callback <CommandMessage>(e => message = e);

            var sender = new CommandPublisher(contextMock.Object);

            var command = new TestCommand(destQueue);

            var jsonBody = JsonConvert.SerializeObject(command);

            // Act
            sender.PublishAsync <TestCommand>(command);

            // Assert
            Assert.AreEqual(destQueue, message.DestinationQueue);
            Assert.AreEqual(command.Id, message.CorrelationId);
            Assert.AreEqual(command.Timestamp, message.Timestamp);
            Assert.AreEqual(jsonBody, Encoding.Unicode.GetString(message.Body));
        }
        public void CommandReturnsProperException(string message)
        {
            // Arrange
            using var busContext = new RabbitMqContextBuilder()
                                   .WithExchange("TestExchange")
                                   .WithConnectionString("amqp://*****:*****@localhost")
                                   .CreateContext();

            using var host = new MicroserviceHostBuilder()
                             .WithBusContext(busContext)
                             .WithQueueName("QueueName2")
                             .AddEventListener <ErrorEventListener>()
                             .CreateHost();

            host.Start();

            var command   = new DummyCommand(message);
            var publisher = new CommandPublisher(busContext);

            // Act
            Task <DummyCommand> Act() => publisher.PublishAsync <DummyCommand>(command);

            // Arrange
            var exception = Assert.ThrowsExceptionAsync <DestinationQueueException>(Act);

            Assert.AreEqual("Received error command from queue Test.Command.Listener", exception.Result.Message);
        }
Example #4
0
        public void UseConventionsRegistersProperCommandListeners()
        {
            // Arrange
            var testContext = new TestBusContext();
            var hostBuilder = new MicroserviceHostBuilder()
                              .WithBusContext(testContext);

            // Act
            hostBuilder.UseConventions();

            hostBuilder.CreateHost().Start();

            // Assert
            var message = new GetAnimalsCommand();

            ICommandPublisher publisher = new CommandPublisher(testContext);

            Animal[] animals = publisher.PublishAsync <IEnumerable <Animal> >(message).Result.ToArray();
            Assert.AreEqual(2, animals.Length);
        }
        public void CommandListenerWithNullReturnsNullProperly(string text)
        {
            // Arrange
            var testContext = new TestBusContext();
            var hostBuilder = new MicroserviceHostBuilder().WithBusContext(testContext);

            using var host = hostBuilder
                             .WithQueueName("test.queue")
                             .AddEventListener <NullCommandListener>()
                             .CreateHost();

            host.Start();

            var publisher = new CommandPublisher(testContext);
            var message   = new DummyCommand {
                Text = text
            };

            //Act
            var result = publisher.PublishAsync <DummyCommand>(message);

            // Assert
            Assert.IsNull(result.Result);
        }
Example #6
0
        /**
         * An example implementation on how to set up a
         * MicroserviceHost with RabbitMQ in the Main method
         *
         * Setting up a functioning RabbitMQ instance with docker is as easy as running:
         * docker run -d -p 15672:15672 -p 5672:5672 rabbitmq:3-management
         */
        static void Main(string[] args)
        {
            /*
             * Logging is quite important, for this reason we first create
             * a loggerfactory that will be used in the rest of the application
             *
             * This one outputs to the console
             */
            using ILoggerFactory loggerFactory = LoggerFactory.Create(configure =>
            {
                configure.AddConsole().SetMinimumLevel(LogLevel.Error);
            });

            /*
             * To reach the library classes, a static class has been put in place
             * to register a logger
             */
            MiffyLoggerFactory.LoggerFactory    = loggerFactory;
            RabbitMqLoggerFactory.LoggerFactory = loggerFactory;

            /*
             * Now that the logger is done, let's create a RabbitMq context with
             * an exchange called MVM.EventExchange and a connection string
             * that connects to a local RabbitMQ instance
             */
            RabbitMqContextBuilder contextBuilder = new RabbitMqContextBuilder()
                                                    .WithExchange("MVM.EventExchange")
                                                    .WithConnectionString("amqp://*****:*****@localhost");

            /*
             * Now instantiate the context and ensure that it's disposed of by using a
             * 'using' statement.
             */
            using IBusContext <IConnection> context = contextBuilder.CreateContext();


            /**
             * Create a dummy database context for testing with an in-memory database
             */
            PolisContext databaseContext = new PolisContext();

            /**
             * Now create a builder that will build our microservice host.
             *
             * First, register our logger by setting the logger factory,
             * next we register any dependencies we might need, like a DBContext
             * that is injected into our CommandListeners and EventListeners
             *
             * Since most of the inner workings of this framework are asynchronous, we
             * strongly urge you to set up a loggerfactory to quickly find any errors going over the bus.
             *
             * Then, throw our context into the builder and lastly, ensure that
             * all our event/command listeners are registered by calling UseConventions().
             *
             * UseConventions could be replaced by multiple AddEventListener calls.
             */
            MicroserviceHostBuilder builder = new MicroserviceHostBuilder()
                                              .SetLoggerFactory(loggerFactory)
                                              // This method also accepts a service collection as input
                                              .RegisterDependencies(services =>
            {
                services.AddSingleton(databaseContext);
            })
                                              .WithQueueName("ExampleService.Queue")
                                              .WithBusContext(context)
                                              .UseConventions();

            /**
             * Lastly, instantiate a host and ensure it starts
             */
            using IMicroserviceHost host = builder.CreateHost();
            host.Start();

            /**
             * Let's add a C# event listener for fun
             */
            host.EventMessageReceived += (message, eventArgs) =>
            {
                Console.WriteLine("Received a EventMessage!");
            };

            /**
             * Now let's pretend this service is running somewhere in a cluster
             * and is receiving events, let's fire some events at it
             */
            string[]        names     = { "Jack", "Jake", "Penny", "Robin", "Rick", "Vinny", "Spencer" };
            IEventPublisher publisher = new EventPublisher(context, loggerFactory);

            foreach (string name in names)
            {
                PolisToegevoegdEvent toegevoegdEvent = new PolisToegevoegdEvent
                {
                    Polis = new Polis {
                        Klantnaam = name
                    }
                };
                publisher.PublishAsync(toegevoegdEvent);
            }

            /**
             * Now let's wait 1 second for all the events to arrive and be processed
             */
            Thread.Sleep(2000);

            /**
             * Now let's fire a command and retrieve a list of polissen
             */
            ICommandPublisher           commandPublisher = new CommandPublisher(context, loggerFactory);
            HaalPolissenOpCommand       command          = new HaalPolissenOpCommand();
            HaalPolissenOpCommandResult commandResult    = commandPublisher.PublishAsync <HaalPolissenOpCommandResult>(command).Result;

            /**
             * Now, print the result!
             */
            foreach (Polis polis in commandResult.Polissen)
            {
                Console.WriteLine($"Found polis for {polis.Klantnaam} with ID {polis.Id}");
            }

            /**
             * Now let's see what happens if we send a mangled json event
             *
             * This should log a critical error in the console and tell you how you might be able to diagnose the problem.
             */
            publisher.Publish(0, "MVM.Polisbeheer.PolisToegevoegd", Guid.Empty, "PolisToegevoegdEvent", "{[somerandomjson}]");

            /**
             * Lastly, let's see how the queue deals with exceptions on the other side
             */
            ExceptionCommand exceptionCommand = new ExceptionCommand();

            try
            {
                /**
                 * This will throw an exception in the receiver
                 */
                _ = commandPublisher.PublishAsync <ExceptionCommand>(exceptionCommand).Result;
            }
            catch (AggregateException expectedException)
            {
                DestinationQueueException destinationQueueException = expectedException.InnerExceptions.First() as DestinationQueueException;

                /**
                 * Now the expectedException.Innerexception will reveal all the info we need
                 */
                Console.WriteLine($"{destinationQueueException?.InnerException?.GetType().Name} {destinationQueueException?.InnerException?.Message}");
                Console.WriteLine($"Destination queue: {destinationQueueException?.DestinationQueueName}, " +
                                  $"Reply queue: {destinationQueueException?.ReplyQueueName}, " +
                                  $"Id: {destinationQueueException?.CorrelationId}");
            }
        }
        /// <summary>
        /// Publish a replay event command and initiate a replay
        /// </summary>
        public virtual void Initiate(ReplayEventsCommand command)
        {
            Logger.LogInformation($"Initiating replay from {command.FromTimeStamp} to {command.ToTimeStamp}, " +
                                  $"Topic {string.Join(',', command.Topics)}, " +
                                  $"Types {string.Join(',', command.Types)}, " +
                                  $"DestinationQueue {command.DestinationQueue}. " +
                                  $"Process ID {command.ProcessId} and Timestamp {command.Timestamp}");

            if (Host.IsListening && !Host.IsPaused)
            {
                Logger.LogDebug("Pausing host");
                Host.Pause();
            }

            Logger.LogTrace("Setting up manual reset events");
            ManualResetEvent startResetEvent = new ManualResetEvent(false);
            ManualResetEvent endResetEvent   = new ManualResetEvent(false);

            Logger.LogTrace("Setting up random queue names");
            string startQueue = Guid.NewGuid().ToString();
            string endQueue   = Guid.NewGuid().ToString();

            Logger.LogDebug($"Setting up replay startlistener with topic {ReplayTopicNames.ReplayStartEventTopic} " +
                            $"and queue {startQueue}");

            MicroserviceReplayListener startListener = new MicroserviceReplayListener
            {
                Callback = callbackResult =>
                {
                    Logger.LogDebug("Received replay start event, deserializing and checking process id...");
                    string      data        = Encoding.Unicode.GetString(callbackResult.Body);
                    DomainEvent domainEvent = JsonConvert.DeserializeObject <DomainEvent>(data);

                    if (domainEvent.ProcessId.ToString() != command.ProcessId.ToString())
                    {
                        return;
                    }

                    Logger.LogInformation($"Start event received with process id {command.ProcessId}");
                    startResetEvent.Set();
                },
                Queue            = startQueue,
                TopicExpressions = new [] { ReplayTopicNames.ReplayStartEventTopic }
            };

            Logger.LogDebug($"Setting up replay endlistener with topic {ReplayTopicNames.ReplayEndEventTopic} " +
                            $"and queue {endQueue}");

            MicroserviceReplayListener endListener = new MicroserviceReplayListener
            {
                Callback = callbackResult =>
                {
                    Logger.LogDebug("Received replay end event, deserializing and checking process id...");
                    string      data        = Encoding.Unicode.GetString(callbackResult.Body);
                    DomainEvent domainEvent = JsonConvert.DeserializeObject <DomainEvent>(data);

                    if (domainEvent.ProcessId.ToString() != command.ProcessId.ToString())
                    {
                        return;
                    }

                    Logger.LogInformation($"End event received with process id {command.ProcessId}");
                    endResetEvent.Set();
                },
                Queue            = endQueue,
                TopicExpressions = new [] { ReplayTopicNames.ReplayEndEventTopic }
            };

            Logger.LogTrace("Setting up start and end listeners in host");
            Host.StartListener = startListener;
            Host.EndListener   = endListener;

            Logger.LogTrace("Starting replay");
            Host.StartReplay();

            Logger.LogDebug("Publishing replay command to auditlogger");
            ReplayEventsResult result = CommandPublisher.PublishAsync <ReplayEventsResult>(command).Result;

            Logger.LogInformation($"{result.AmountOfEvents} event(s) will be replayed");
            Logger.LogDebug("Awaiting start event");

            startResetEvent.WaitOne();
            endResetEvent.WaitOne();

            Logger.LogDebug("End event received, ending replay");
            Host.StopReplay();

            if (!Host.IsListening)
            {
                return;
            }

            Logger.LogDebug("Resuming host");
            Host.Resume();
        }