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); }
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); }
/** * 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(); }