private async Task <WorkflowProcessingResult> ProcessMessageInternal(IWorkflowMessage workflowMessage, CancellationToken cancellationToken) { // TODO: this need to be removed Guard.ArgumentNotNull(workflowMessage, nameof(workflowMessage)); Guard.ArgumentNotNull(workflowMessage.WorkflowInstanceId, nameof(workflowMessage.WorkflowInstanceId)); Debug.Assert(workflowMessage.WorkflowInstanceId != null, "workflowMessage.WorkflowInstanceId != null"); var workflowInstance = await GetWorkflowInstanceWithLock(workflowMessage.WorkflowInstanceId.Value, cancellationToken).ConfigureAwait(false); if (null == workflowInstance) { throw new WorkflowException($"Workflow message [{workflowMessage.WorkflowMessageId:D}] is referring to non existing workflow instance"); } var workflowConfiguration = await GetWorkflowConfiguration(workflowInstance.WorkflowId, cancellationToken).ConfigureAwait(false); Guard.ArgumentNotNull(workflowConfiguration, nameof(workflowConfiguration)); var stateConfiguration = workflowConfiguration.GetStateConfigurationByCode(workflowInstance.CurrentStateCode); Guard.ArgumentNotNull(stateConfiguration, nameof(stateConfiguration)); var workflowContext = new WorkflowContext(this, WorkflowEngineBuilder, workflowInstance, workflowConfiguration); // ensure that domain entity is available for all/any coded logic for read-only purposes await TryGetDomainEntity(workflowContext, cancellationToken).ConfigureAwait(false); var messageExecutionContext = new MessageExecutionContext(workflowContext, workflowMessage); var messageExecutionResult = await _messageProcessorLazy.Value.ProcessMessage(messageExecutionContext, cancellationToken).ConfigureAwait(false); if (messageExecutionResult.Status == MessageExecutionStatus.Failed) { Log.Error("An error has occurred during processing message [{messageId}] for {workflowInstanceId}", workflowMessage.WorkflowMessageId, workflowInstance.Id); await TransitionToState(workflowContext, workflowConfiguration.GetFailedStateConfiguration(), null, cancellationToken).ConfigureAwait(false); return(new WorkflowProcessingResult(workflowInstance, workflowContext.WorkflowExecutionState)); } if (messageExecutionResult.Status == MessageExecutionStatus.ContinueAfterAsyncTransition) { if (workflowInstance.CurrentStateProgress != StateExecutionProgress.AwaitingAsyncTransition) { Log.Error("Can not process message [{messageId}] for {workflowInstanceId} because workflow instance is not in [{subState}]", workflowMessage.WorkflowMessageId, workflowInstance.Id, StateExecutionProgress.AwaitingAsyncTransition); await TransitionToState(workflowContext, workflowConfiguration.GetFailedStateConfiguration(), null, cancellationToken).ConfigureAwait(false); return(new WorkflowProcessingResult(workflowInstance, workflowContext.WorkflowExecutionState)); } workflowContext.WorkflowInstance.CurrentStateProgress = StateExecutionProgress.AfterAsyncTransition; await ProcessState(workflowContext, stateConfiguration, cancellationToken).ConfigureAwait(false); } return(new WorkflowProcessingResult(workflowInstance, workflowContext.WorkflowExecutionState)); }
public async void Subscribe_MalformedEvent_ExceptionShouldBeThrown() { //prepare var messagebytes = Encoding.UTF8.GetBytes(@"{ ""Version"": ""0.1"" }"); EventEmulator eventEmulator = null; _mockBus.Setup(x => x.Consume(_mockQueue.Object, It.IsAny <Func <byte[], MessageProperties, MessageReceivedInfo, Task> >(), It.IsAny <Action <IConsumerConfiguration> >())) .Returns((IQueue queue, Func <byte[], MessageProperties, MessageReceivedInfo, Task> func, Action <IConsumerConfiguration> configure) => { eventEmulator = new EventEmulator(func); return(_consumerDisposable.Object); }); Dictionary <string, ISubscription> subscriptions = new Dictionary <string, ISubscription>(); MessageProperties messageProperties = new MessageProperties(); MessageExecutionContext actualMessageExecutionContext = null; _errorHandlingStrategy.Setup(x => x.HandleErrorAsync( It.IsAny <MessageExecutionContext>())) .Returns <MessageExecutionContext>( (executionContext) => { actualMessageExecutionContext = executionContext; return(Task.FromException <MalformedEventException>(executionContext.Exception)); }); _easyNetQSubscriber.Subscribe(subscriptions); //Act await eventEmulator.Execute(messagebytes, messageProperties, null); //check ((MalformedEventException)eventEmulator.RaisedException). Errors.First().ShouldBeEquivalentTo("EventId token is missed"); _successHandlingStrategy.Verify(x => x.HandleSuccessAsync(It.IsAny <IntegrationEvent>()), Times.Never); _errorHandlingStrategy.Verify(x => x.HandleErrorAsync(It.IsAny <MessageExecutionContext>()), Times.Once); actualMessageExecutionContext.BodyBytes.ShouldBeEquivalentTo(messagebytes); actualMessageExecutionContext.Exception.Should().Be(eventEmulator.RaisedException); actualMessageExecutionContext.MessageProperties.Should().Be(messageProperties); actualMessageExecutionContext.IntergrationEventParsingResult.ShouldBeEquivalentTo(DefaultIntergrationEventParser.Parse(messagebytes)); actualMessageExecutionContext.SerializedMessage.Should().BeNull(); actualMessageExecutionContext.DeadLetterIntegrationEvent.Should().BeNull(); actualMessageExecutionContext.Subscription.Should().BeNull(); }
public async void Subscribe_DiscardEventException_WhenProcessed_ExceptionShouldBeThrown() { //prepare var content = new BookingCreated() { BookingName = _bookingName }; IntegrationEvent <BookingCreated> integrationEvent = new IntegrationEvent <BookingCreated> { CorrelationId = Guid.NewGuid(), EventCreationDate = DateTime.UtcNow, EventId = Guid.NewGuid(), EventType = _bookingEventType, Version = _version, Content = content }; var messagebytes = SerializeEvent(integrationEvent); EventEmulator eventEmulator = null; _mockBus.Setup(x => x.Consume(_mockQueue.Object, It.IsAny <Func <byte[], MessageProperties, MessageReceivedInfo, Task> >(), It.IsAny <Action <IConsumerConfiguration> >())) .Returns((IQueue queue, Func <byte[], MessageProperties, MessageReceivedInfo, Task> func, Action <IConsumerConfiguration> configure) => { eventEmulator = new EventEmulator(func); return(_consumerDisposable.Object); }); DiscardEventException discardEventException = new DiscardEventException(); var mockSubscription = new Mock <ISubscription>(); mockSubscription.Setup(x => x.InvokeAsync(Encoding.UTF8.GetString(messagebytes))) .Returns <string>( (serilizedmessage) => { return(Task.FromException <DiscardEventException>(discardEventException)); }); Dictionary <string, ISubscription> subscriptions = new Dictionary <string, ISubscription> { { "bookingcreated", mockSubscription.Object } }; _subsciptionSelector.Setup(x => x.Select(subscriptions, "bookingcreated")) .Returns((IDictionary <string, ISubscription> subs, string eventType) => mockSubscription.Object); MessageProperties messageProperties = new MessageProperties(); MessageExecutionContext actualMessageExecutionContext = null; _errorHandlingStrategy.Setup(x => x.HandleErrorAsync(It.IsAny <MessageExecutionContext>())) .Returns <MessageExecutionContext>( (messageExecutionContext) => { actualMessageExecutionContext = messageExecutionContext; return(Task.FromException <DiscardEventException>(messageExecutionContext.Exception)); }); _easyNetQSubscriber.Subscribe(subscriptions); //Act await eventEmulator.Execute(messagebytes, messageProperties, null); //check eventEmulator.RaisedException.GetType().Should().Be(typeof(DiscardEventException)); _successHandlingStrategy.Verify(x => x.HandleSuccessAsync(It.IsAny <IntegrationEvent>()), Times.Never); _errorHandlingStrategy.Verify(x => x.HandleErrorAsync(It.IsAny <MessageExecutionContext>()), Times.Once); actualMessageExecutionContext.BodyBytes.ShouldBeEquivalentTo(messagebytes); actualMessageExecutionContext.Exception.Should().Be(eventEmulator.RaisedException); actualMessageExecutionContext.MessageProperties.Should().Be(messageProperties); actualMessageExecutionContext.IntergrationEventParsingResult.ShouldBeEquivalentTo(DefaultIntergrationEventParser.Parse(messagebytes)); actualMessageExecutionContext.SerializedMessage.Should().Be(Encoding.UTF8.GetString(messagebytes)); actualMessageExecutionContext.DeadLetterIntegrationEvent.Should().BeNull(); actualMessageExecutionContext.Subscription.Should().Be(mockSubscription.Object); }
public async void Subscribe_CorrectlyFormedEvent_NoConsumerForEventTypeMatched() { //prepare IntegrationEvent <BookingCreated> integrationEvent = new IntegrationEvent <BookingCreated>() { CorrelationId = Guid.NewGuid(), EventCreationDate = DateTime.UtcNow, EventId = Guid.NewGuid(), EventType = _bookingEventType, Version = _version, Content = new BookingCreated { BookingName = _bookingName } }; var messagebytes = SerializeEvent(integrationEvent); EventEmulator eventEmulator = null; _mockBus.Setup(x => x.Consume(_mockQueue.Object, It.IsAny <Func <byte[], MessageProperties, MessageReceivedInfo, Task> >(), It.IsAny <Action <IConsumerConfiguration> >())) .Returns((IQueue queue, Func <byte[], MessageProperties, MessageReceivedInfo, Task> func, Action <IConsumerConfiguration> configure) => { eventEmulator = new EventEmulator(func); return(_consumerDisposable.Object); }); Dictionary <string, ISubscription> subscriptions = new Dictionary <string, ISubscription>(); _subsciptionSelector.Setup(x => x.Select(subscriptions, _bookingEventType)) .Returns((IDictionary <string, ISubscription> subs, string eventType) => null); MessageProperties messageProperties = new MessageProperties(); MessageExecutionContext actualMessageExecutionContext = null; _errorHandlingStrategy.Setup(x => x.HandleErrorAsync(It.IsAny <MessageExecutionContext>())) .Returns <MessageExecutionContext>( (messageExecutionContext) => { actualMessageExecutionContext = messageExecutionContext; return(Task.FromException <DiscardEventException>(messageExecutionContext.Exception)); }); _easyNetQSubscriber.Subscribe(subscriptions); //Act await eventEmulator.Execute(messagebytes, messageProperties, null); //check _successHandlingStrategy.Verify(x => x.HandleSuccessAsync(It.IsAny <IntegrationEvent>()), Times.Never); _errorHandlingStrategy.Verify(x => x.HandleErrorAsync(It.IsAny <MessageExecutionContext>()), Times.Once); actualMessageExecutionContext.BodyBytes.ShouldBeEquivalentTo(messagebytes); actualMessageExecutionContext.Exception.Should().Be(eventEmulator.RaisedException); actualMessageExecutionContext.MessageProperties.Should().Be(messageProperties); actualMessageExecutionContext.IntergrationEventParsingResult.ShouldBeEquivalentTo(DefaultIntergrationEventParser.Parse(messagebytes)); actualMessageExecutionContext.SerializedMessage.Should().BeNull(); actualMessageExecutionContext.DeadLetterIntegrationEvent.Should().BeNull(); actualMessageExecutionContext.Subscription.Should().BeNull(); eventEmulator.RaisedException.Should().BeOfType <DiscardEventException>().Which.Message .ShouldBeEquivalentTo("The consumer for bookingcreated event type is not matched"); }
public async void HandleErrorAsync_ExceptionThatShouldBeCatchedANDAttemptNumberN_MessageShouldBeSentToAppropriateRetryQueue(bool explicitAcknowledgments, int numberOfAttempt, bool storedeadletter) { //prepare MessageProperties messageProperties = new MessageProperties(); if (numberOfAttempt != 0) { messageProperties.Headers = new Dictionary <string, object>(); messageProperties.Headers["retrycount"] = numberOfAttempt; } BookingCreated bookingCreated = new BookingCreated { BookingName = "test" }; string bookingCreatedEventType = "bookingCreatedEventType"; IntegrationEvent <BookingCreated> integrationEvent = new IntegrationEvent <BookingCreated>(bookingCreated, bookingCreatedEventType); var message = JsonConvert.SerializeObject(integrationEvent); var body = Encoding.UTF8.GetBytes(message); Exception exception = new IOException("IOException"); ExecutionHandlingStrategy executionHandlingStrategy = new ExecutionHandlingStrategy(_subscriberName, _easyNetQPublisher, _maxRetry, _modelNamingConventionController.Object, explicitAcknowledgments, _publisher.Object, storedeadletter); string retryRoutingKey = string.Concat("retry.", numberOfAttempt + 1, ".", _subscriberName); _modelNamingConventionController.Setup(x => x.GetRetryRoutingKey(_subscriberName, numberOfAttempt + 1)) .Returns((string subscribername, int retryindex) => retryRoutingKey); string actualRouting = string.Empty; MessageProperties actualProperties = null; byte[] actualMsg = null; IExchange actualExchange = null; _mockBus.Setup(x => x.ExchangeDeclareAsync(_exchangeName, ExchangeType.Topic, false, true, false, false, null, false)) .Returns((string exchangename, string exchanegType, bool passive, bool durable, bool autoDelete, bool internalflag, string alternateExchange, bool delayed) => { return(Task.FromResult(_mockExchange.Object)); }); _mockBus.Setup(x => x.PublishAsync(It.IsAny <IExchange>(), It.IsAny <string>(), false, It.IsAny <MessageProperties>(), It.IsAny <byte[]>())) .Returns((IExchange exchange, string routing, bool mandatory, MessageProperties properties, byte[] msg) => { return(Task.FromResult(true)); }) .Callback <IExchange, string, bool, MessageProperties, byte[]>( (exchange, routing, mandatory, properties, msg) => { actualExchange = exchange; actualRouting = routing; actualProperties = properties; actualMsg = msg; }); //act Mock <ISubscription> mockedSubscription = new Mock <ISubscription>(); MessageExecutionContext messageExecutionContext = new MessageExecutionContext(body, messageProperties, null) { Exception = exception, SerializedMessage = message, Subscription = mockedSubscription.Object, IntergrationEventParsingResult = null, DeadLetterIntegrationEvent = null }; await executionHandlingStrategy.HandleErrorAsync(messageExecutionContext); //check actualRouting.Should().Be(retryRoutingKey); actualMsg.Should().BeEquivalentTo(body); (int.Parse(actualProperties.Headers["retrycount"].ToString())).Should().Be(numberOfAttempt + 1); actualExchange.Should().Be(_mockExchange.Object); _publisher.Verify(m => m.PublishEventAsync(It.IsAny <IntegrationEvent <string> >(), It.IsAny <string>(), It.IsAny <bool>()), Times.Never); mockedSubscription.Verify(s => s.NotifyAboutDeadLetterAsync(It.IsAny <string>(), It.IsAny <Exception>()), Times.Never); mockedSubscription.Verify(s => s.InvokeAsync(It.IsAny <string>()), Times.Never); }
public async void HandleErrorAsync_ExceptionThatShouldBeCatchedANDLastAttempt_MessageShouldBeSentToAppropriateDeadLetterQueue(bool explicitAcknowledgments, bool storedeadletter) { //prepare MessageProperties messageProperties = new MessageProperties(); messageProperties.Headers = new Dictionary <string, object>(); messageProperties.Headers["retrycount"] = _maxRetry; BookingCreated bookingCreated = new BookingCreated { BookingName = "test" }; string bookingCreatedEventType = "bookingCreatedEventType"; IntegrationEvent <BookingCreated> integrationEvent = new IntegrationEvent <BookingCreated>(bookingCreated, bookingCreatedEventType); var message = JsonConvert.SerializeObject(integrationEvent); var body = Encoding.UTF8.GetBytes(message); Exception exception = new IOException("IOException"); ExecutionHandlingStrategy executionHandlingStrategy = new ExecutionHandlingStrategy(_subscriberName, _easyNetQPublisher, _maxRetry, _modelNamingConventionController.Object, explicitAcknowledgments, _publisher.Object, storedeadletter); _mockBus.Setup(x => x.ExchangeDeclareAsync(_exchangeName, ExchangeType.Topic, false, true, false, false, null, false)) .Returns((string exchangename, string exchanegType, bool passive, bool durable, bool autoDelete, bool internalflag, string alternateExchange, bool delayed) => { return(Task.FromResult(_mockExchange.Object)); }); _modelNamingConventionController.Setup(x => x.GetDeadLetterQueueMessageEventType(bookingCreatedEventType)) .Returns((string eventType) => string.Concat("deadletter.", eventType)); _modelNamingConventionController.Setup(x => x.GetDeadLetterQueueRoutingKey(_subscriberName)) .Returns((string subscriberName) => string.Concat("deadletter.", subscriberName)); //Main flow string actualRouting = string.Empty; MessageProperties actualProperties = null; byte[] actualMsg = null; IExchange actualExchange = null; _mockBus.Setup(x => x.PublishAsync(It.IsAny <IExchange>(), It.IsAny <string>(), false, It.IsAny <MessageProperties>(), It.IsAny <byte[]>())) .Returns((IExchange exchange, string routing, bool mandatory, MessageProperties properties, byte[] msg) => { return(Task.FromResult(true)); }) .Callback <IExchange, string, bool, MessageProperties, byte[]>( (exchange, routing, mandatory, properties, msg) => { actualExchange = exchange; actualRouting = routing; actualProperties = properties; actualMsg = msg; }); //END Main flow //Tracking flow IntegrationEvent <DeadLetterEventDescriptor <JObject> > actualTrackingMessage = null; string actualTrackingMessageRouting = string.Empty; bool actualPersistantMode = false; _publisher.Setup(x => x.PublishEventAsync(It.IsAny <IntegrationEvent <DeadLetterEventDescriptor <JObject> > >(), It.IsAny <string>(), true)) .Returns(Task.FromResult(true)) .Callback <IntegrationEvent <DeadLetterEventDescriptor <JObject> >, string, bool>( (trackingMessage, routing, persistantMode) => { actualTrackingMessage = trackingMessage; actualTrackingMessageRouting = routing; actualPersistantMode = persistantMode; }); //END Tracking flow //act Mock <ISubscription> mockedSubscription = new Mock <ISubscription>(); MessageExecutionContext messageExecutionContext = new MessageExecutionContext(body, messageProperties, null) { Exception = exception, SerializedMessage = message, Subscription = mockedSubscription.Object, IntergrationEventParsingResult = null, DeadLetterIntegrationEvent = null }; await executionHandlingStrategy.HandleErrorAsync(messageExecutionContext); //check var expectedDeadLetterEventDescriptor = new DeadLetterEventDescriptor <JObject> { CountOfAttempts = _maxRetry, LastAttemptError = exception.ToString(), Original = JsonConvert.DeserializeObject <IntegrationEvent <JObject> >(message) }; var expectedDeadletterqueueevent = new IntegrationEvent <DeadLetterEventDescriptor <JObject> > { CorrelationId = integrationEvent.CorrelationId, EventType = string.Concat("deadletter.", bookingCreatedEventType), Content = expectedDeadLetterEventDescriptor }; //Check publishing dead letter message if (storedeadletter) { var actuaDeadLetterQueuelSerializedMessage = Encoding.UTF8.GetString(actualMsg); IntegrationEvent <DeadLetterEventDescriptor <JObject> > actualDeadLetterQueueIntegrationEvent = JsonConvert.DeserializeObject <IntegrationEvent <DeadLetterEventDescriptor <JObject> > >(actuaDeadLetterQueuelSerializedMessage); actualDeadLetterQueueIntegrationEvent.CorrelationId.Should().Be(expectedDeadletterqueueevent.CorrelationId); actualDeadLetterQueueIntegrationEvent.Content.ShouldBeEquivalentTo(expectedDeadletterqueueevent.Content); actualDeadLetterQueueIntegrationEvent.EventType.Should() .Be(expectedDeadletterqueueevent.EventType); actualRouting.Should().Be(string.Concat("deadletter.", _subscriberName)); actualExchange.Should().Be(_mockExchange.Object); actualProperties.DeliveryMode.Should().Be(2);//persistant delivery mode } else { _mockBus.Verify(v => v.PublishAsync(It.IsAny <IExchange>(), It.IsAny <string>(), false, It.IsAny <MessageProperties>(), It.IsAny <byte[]>()), Times.Never); } //Check Subsciption notification mockedSubscription.Verify(s => s.NotifyAboutDeadLetterAsync(messageExecutionContext.SerializedMessage, messageExecutionContext.Exception), Times.Once); mockedSubscription.Verify(s => s.InvokeAsync(It.IsAny <string>()), Times.Never); //Check Tracking flow if (explicitAcknowledgments == false) { //check, that No deadletter message for tracking is sent. _publisher.Verify(v => v.PublishEventAsync(It.IsAny <IntegrationEvent <DeadLetterEventDescriptor <JObject> > >(), It.IsAny <string>(), It.IsAny <bool>()), Times.Never); } else { //check, that deadltter message for tracking is equal to the one sent to the deadletter queue. Routing key must be the same _publisher.Verify(v => v.PublishEventAsync(It.IsAny <IntegrationEvent <DeadLetterEventDescriptor <JObject> > >(), It.IsAny <string>(), It.IsAny <bool>()), Times.Once); actualTrackingMessageRouting.Should().Be(string.Concat("deadletter.", _subscriberName)); actualPersistantMode.Should().BeTrue(); actualTrackingMessage.CorrelationId.Should().Be(expectedDeadletterqueueevent.CorrelationId); actualTrackingMessage.Content.ShouldBeEquivalentTo(expectedDeadletterqueueevent.Content); actualTrackingMessage.EventType.Should() .Be(expectedDeadletterqueueevent.EventType); } }