예제 #1
0
        public async Task CopyTo_MessageStreamCopied()
        {
            var streamProvider = new MessageStreamProvider <IRawInboundEnvelope>();
            var chunkStream    = new ChunkStream(streamProvider.CreateStream <IRawInboundEnvelope>());
            var output         = new MemoryStream(Encoding.UTF8.GetBytes("some junk-"));

            var copyTask = Task.Run(() => chunkStream.CopyTo(output));

            await streamProvider.PushAsync(
                new RawInboundEnvelope(
                    Encoding.UTF8.GetBytes("Silver"),
                    null,
                    TestConsumerEndpoint.GetDefault(),
                    "test",
                    new TestOffset()));

            await streamProvider.PushAsync(
                new RawInboundEnvelope(
                    Encoding.UTF8.GetBytes("back"),
                    null,
                    TestConsumerEndpoint.GetDefault(),
                    "test",
                    new TestOffset()));

            await streamProvider.CompleteAsync();

            await copyTask;

            chunkStream.Close();

            Encoding.UTF8.GetString(output.ToArray()).Should().Be("Silverback");
        }
        public void CanHandle_Whatever_TrueReturned(int failedAttempts)
        {
            var rawMessage = new MemoryStream();
            var headers    = new[]
            {
                new MessageHeader(
                    DefaultMessageHeaders.FailedAttempts,
                    failedAttempts.ToString(CultureInfo.InvariantCulture))
            };

            var testPolicy = new TestErrorPolicy();

            var chain = new ErrorPolicyChain(
                new RetryErrorPolicy().MaxFailedAttempts(3),
                testPolicy)
                        .Build(_serviceProvider);

            var result = chain.CanHandle(
                ConsumerPipelineContextHelper.CreateSubstitute(
                    new InboundEnvelope(
                        rawMessage,
                        headers,
                        new TestOffset(),
                        TestConsumerEndpoint.GetDefault(),
                        TestConsumerEndpoint.GetDefault().Name)),
                new InvalidOperationException("test"));

            result.Should().BeTrue();
        }
예제 #3
0
        public async Task HandleErrorAsync_Whatever_ConsumerRolledBackAndTransactionAborted()
        {
            var policy   = new RetryErrorPolicy().MaxFailedAttempts(3).Build(_serviceProvider);
            var envelope = new InboundEnvelope(
                "hey oh!",
                new MemoryStream(),
                null,
                new TestOffset(),
                TestConsumerEndpoint.GetDefault(),
                TestConsumerEndpoint.GetDefault().Name);

            var transactionManager = Substitute.For <IConsumerTransactionManager>();

            await policy.HandleErrorAsync(
                ConsumerPipelineContextHelper.CreateSubstitute(
                    envelope,
                    _serviceProvider,
                    transactionManager),
                new InvalidOperationException("test"));

            await transactionManager.Received(1).RollbackAsync(
                Arg.Any <InvalidOperationException>(),
                false,
                true,
                false);
        }
        public async Task HandleAsync_ThrowException_ExceptionIsThrown()
        {
            var message = new TestValidationMessage
            {
                Id = "1", String10 = "123456789abc", IntRange = 5, NumbersOnly = "123"
            };
            var expectedMessage =
                $"The message is not valid:{Environment.NewLine}- The field String10 must be a string with a maximum length of 10.";
            var endpoint = TestConsumerEndpoint.GetDefault();

            endpoint.MessageValidationMode = MessageValidationMode.ThrowException;

            var envelope = new InboundEnvelope(
                message,
                null,
                null,
                new TestOffset(),
                endpoint,
                "source-endpoint");

            IRawInboundEnvelope?result = null;
            Func <Task>         act    = () => new ValidatorConsumerBehavior(_inboundLogger).HandleAsync(
                ConsumerPipelineContextHelper.CreateSubstitute(envelope, _serviceProvider),
                context =>
            {
                result = context.Envelope;
                return(Task.CompletedTask);
            });

            await act.Should().ThrowAsync <MessageValidationException>().WithMessage(expectedMessage);

            result.Should().BeNull();
        }
        public async Task HandleAsync_LogWarning_WarningIsLogged(
            IIntegrationMessage message,
            string expectedValidationMessage)
        {
            var endpoint = TestConsumerEndpoint.GetDefault();

            endpoint.MessageValidationMode = MessageValidationMode.LogWarning;

            var envelope = new InboundEnvelope(
                message,
                null,
                null,
                new TestOffset(),
                endpoint,
                "source-endpoint");

            IRawInboundEnvelope?result = null;

            await new ValidatorConsumerBehavior(_inboundLogger).HandleAsync(
                ConsumerPipelineContextHelper.CreateSubstitute(envelope, _serviceProvider),
                context =>
            {
                result = context.Envelope;
                return(Task.CompletedTask);
            });

            result.Should().NotBeNull();
            _loggerSubstitute.Received(LogLevel.Warning, null, expectedValidationMessage, 1080);
        }
        public async Task HandleErrorAsync_RetryWithMaxFailedAttempts_AppliedAccordingToMaxFailedAttempts(int failedAttempts)
        {
            var rawMessage = new MemoryStream();
            var headers    = new[]
            {
                new MessageHeader(
                    DefaultMessageHeaders.FailedAttempts,
                    failedAttempts.ToString(CultureInfo.InvariantCulture))
            };

            var testPolicy = new TestErrorPolicy();

            var chain = new ErrorPolicyChain(
                new[]
            {
                new RetryErrorPolicy().MaxFailedAttempts(3),
                testPolicy
            })
                        .Build(_serviceProvider);

            await chain.HandleErrorAsync(
                ConsumerPipelineContextHelper.CreateSubstitute(
                    new InboundEnvelope(
                        rawMessage,
                        headers,
                        new TestOffset(),
                        TestConsumerEndpoint.GetDefault(),
                        TestConsumerEndpoint.GetDefault().Name)),
                new InvalidOperationException("test"));

            testPolicy.Applied.Should().Be(failedAttempts > 3);
        }
        public async Task Transform_SingleMessage_HeadersProperlyModified()
        {
            var policy = ErrorPolicy
                         .Move(TestProducerEndpoint.GetDefault())
                         .Transform((outboundEnvelope, ex) => { outboundEnvelope.Headers.Add("error", ex.GetType().Name); })
                         .Build(_serviceProvider);

            var envelope = new InboundEnvelope(
                new MemoryStream(Encoding.UTF8.GetBytes("hey oh!")),
                null,
                new TestOffset(),
                TestConsumerEndpoint.GetDefault(),
                TestConsumerEndpoint.GetDefault().Name);

            envelope.Headers.Add("key", "value");

            await policy.HandleErrorAsync(
                ConsumerPipelineContextHelper.CreateSubstitute(envelope, _serviceProvider),
                new InvalidOperationException("test"));

            var producer   = (TestProducer)_broker.GetProducer(TestProducerEndpoint.GetDefault());
            var newHeaders = producer.ProducedMessages[0].Headers;

            newHeaders.Should().HaveCount(6); // message-id, message-type, key, traceid, error, source-endpoint
        }
        public void CanHandle_Sequence_FalseReturned()
        {
            var policy   = new MoveMessageErrorPolicy(TestProducerEndpoint.GetDefault()).Build(_serviceProvider);
            var envelope = new InboundEnvelope(
                "hey oh!",
                new MemoryStream(),
                null,
                new TestOffset(),
                new TestConsumerEndpoint("test")
            {
                Batch = new BatchSettings
                {
                    Size = 10
                }
            },
                TestConsumerEndpoint.GetDefault().Name);

            var context = ConsumerPipelineContextHelper.CreateSubstitute(envelope, _serviceProvider);

            context.SetSequence(new BatchSequence("batch", context), false);

            var result = policy.CanHandle(
                context,
                new InvalidOperationException("test"));

            result.Should().BeFalse();
        }
        public async Task HandleAsync_NoBinaryFileHeaders_EnvelopeUntouched()
        {
            var rawContent = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 };
            var envelope   = new RawInboundEnvelope(
                rawContent,
                null,
                TestConsumerEndpoint.GetDefault(),
                "test",
                new TestOffset());

            IRawInboundEnvelope?result = null;

            await new BinaryFileHandlerConsumerBehavior().HandleAsync(
                new ConsumerPipelineContext(
                    envelope,
                    Substitute.For <IConsumer>(),
                    Substitute.For <ISequenceStore>(),
                    Substitute.For <IServiceProvider>()),
                context =>
            {
                result = context.Envelope;
                return(Task.CompletedTask);
            });

            result.Should().BeSameAs(envelope);
        }
        public void HandleAsync_WithoutActivityHeaders_NewActivityIsStarted()
        {
            var rawEnvelope = new RawInboundEnvelope(
                new byte[5],
                new MessageHeaderCollection
            {
                { DefaultMessageHeaders.TraceId, "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01" }
            },
                TestConsumerEndpoint.GetDefault(),
                TestConsumerEndpoint.GetDefault().Name,
                new TestOffset());

            var entered = false;

            new ActivityConsumerBehavior().HandleAsync(
                new ConsumerPipelineContext(
                    rawEnvelope,
                    Substitute.For <IConsumer>(),
                    Substitute.For <ISequenceStore>(),
                    Substitute.For <IServiceProvider>()),
                _ =>
            {
                Activity.Current.Should().NotBeNull();
                Activity.Current.Id.Should().NotBeNullOrEmpty();

                entered = true;

                return(Task.CompletedTask);
            });

            entered.Should().BeTrue();
        }
예제 #11
0
        public void CanHandle_WithDifferentFailedAttemptsCount_ReturnReflectsMaxFailedAttempts(
            int failedAttempts,
            bool expectedResult)
        {
            var policy = ErrorPolicy.Retry().MaxFailedAttempts(3).Build(_serviceProvider);

            var rawMessage = new MemoryStream();
            var headers    = new[]
            {
                new MessageHeader(DefaultMessageHeaders.FailedAttempts, failedAttempts)
            };

            var envelope = new InboundEnvelope(
                rawMessage,
                headers,
                new TestOffset(),
                TestConsumerEndpoint.GetDefault(),
                TestConsumerEndpoint.GetDefault().Name);

            var canHandle = policy.CanHandle(
                ConsumerPipelineContextHelper.CreateSubstitute(envelope, _serviceProvider),
                new InvalidOperationException("test"));

            canHandle.Should().Be(expectedResult);
        }
        public async Task HandleAsync_ValidMessage_NoLogAndNoException(MessageValidationMode validationMode)
        {
            var message = new TestValidationMessage
            {
                Id = "1", String10 = "123", IntRange = 5, NumbersOnly = "123"
            };
            var endpoint = TestConsumerEndpoint.GetDefault();

            endpoint.MessageValidationMode = validationMode;

            var envelope = new InboundEnvelope(
                message,
                null,
                null,
                new TestOffset(),
                endpoint,
                "source-endpoint");

            IRawInboundEnvelope?result = null;
            Func <Task>         act    = () => new ValidatorConsumerBehavior(_inboundLogger).HandleAsync(
                ConsumerPipelineContextHelper.CreateSubstitute(envelope, _serviceProvider),
                context =>
            {
                result = context.Envelope;
                return(Task.CompletedTask);
            });
            await act.Should().NotThrowAsync <ValidationException>();

            result.Should().NotBeNull();
            _loggerSubstitute.DidNotReceive(LogLevel.Warning, null).Should().BeTrue();
        }
        public async Task HandleAsync_BinaryFileMessage_BinaryFileMessageReturned()
        {
            var rawContent = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 };
            var headers    = new[]
            {
                new MessageHeader("x-message-type", typeof(BinaryFileMessage).AssemblyQualifiedName)
            };
            var envelope = new RawInboundEnvelope(
                rawContent,
                headers,
                TestConsumerEndpoint.GetDefault(),
                "test",
                new TestOffset());

            IRawInboundEnvelope?result = null;

            await new BinaryFileHandlerConsumerBehavior().HandleAsync(
                new ConsumerPipelineContext(
                    envelope,
                    Substitute.For <IConsumer>(),
                    Substitute.For <ISequenceStore>(),
                    Substitute.For <IServiceProvider>()),
                context =>
            {
                result = context.Envelope;
                return(Task.CompletedTask);
            });

            result.Should().BeAssignableTo <IInboundEnvelope <BinaryFileMessage> >();
            var binaryFileMessage = result.As <IInboundEnvelope <BinaryFileMessage> >().Message !;

            binaryFileMessage.Content.ReadAll().Should().BeEquivalentTo(rawContent);
        }
예제 #14
0
        public void CanHandle_WithMaxFailedAttempts_ExpectedResultReturned(
            int failedAttempts,
            bool expectedResult)
        {
            var envelope = new InboundEnvelope(
                new MemoryStream(),
                new[]
                {
                    new MessageHeader(
                        DefaultMessageHeaders.FailedAttempts,
                        failedAttempts.ToString(CultureInfo.InvariantCulture))
                },
                new TestOffset(),
                TestConsumerEndpoint.GetDefault(),
                TestConsumerEndpoint.GetDefault().Name);

            var policy = new TestErrorPolicy()
                .MaxFailedAttempts(3)
                .Build(Substitute.For<IServiceProvider>());

            var canHandle = policy.CanHandle(
                ConsumerPipelineContextHelper.CreateSubstitute(envelope),
                new InvalidOperationException());

            canHandle.Should().Be(expectedResult);
        }
예제 #15
0
        public async Task HandleAsync_ExceptionThrown_ExceptionLogged()
        {
            var logger            = new LoggerSubstitute <FatalExceptionLoggerConsumerBehavior>();
            var integrationLogger = new SilverbackIntegrationLogger <FatalExceptionLoggerConsumerBehavior>(
                logger,
                new LogTemplates());

            var rawEnvelope = new RawInboundEnvelope(
                new byte[5],
                null,
                TestConsumerEndpoint.GetDefault(),
                TestConsumerEndpoint.GetDefault().Name,
                new TestOffset());

            try
            {
                await new FatalExceptionLoggerConsumerBehavior(integrationLogger).HandleAsync(
                    new ConsumerPipelineContext(
                        rawEnvelope,
                        Substitute.For <IConsumer>(),
                        Substitute.For <ISequenceStore>(),
                        Substitute.For <IServiceProvider>()),
                    _ => throw new InvalidCastException());
            }
            catch
            {
                // Ignored
            }

            logger.Received(LogLevel.Critical, typeof(InvalidCastException));
        }
        public async Task HandleErrorAsync_WithTransform_MessageTranslated()
        {
            var policy = new MoveMessageErrorPolicy(TestProducerEndpoint.GetDefault())
                         .Transform((originalEnvelope, _) => { originalEnvelope.Message = new TestEventTwo(); })
                         .Build(_serviceProvider);

            var rawMessage = new MemoryStream(Encoding.UTF8.GetBytes("hey oh!"));

            var headers = new[]
            {
                new MessageHeader(DefaultMessageHeaders.MessageType, typeof(string).AssemblyQualifiedName)
            };

            var envelope = new InboundEnvelope(
                rawMessage,
                headers,
                new TestOffset(),
                TestConsumerEndpoint.GetDefault(),
                TestConsumerEndpoint.GetDefault().Name);

            await policy.HandleErrorAsync(
                ConsumerPipelineContextHelper.CreateSubstitute(envelope, _serviceProvider),
                new InvalidOperationException("test"));

            var producer = (TestProducer)_broker.GetProducer(TestProducerEndpoint.GetDefault());

            var(producedMessage, _) = await producer.Endpoint.Serializer.DeserializeAsync(
                new MemoryStream(producer.ProducedMessages[0].Message !),
                producer.ProducedMessages[0].Headers,
                MessageSerializationContext.Empty);

            producedMessage.Should().BeOfType <TestEventTwo>();
        }
        public async Task HandleAsync_WithTraceIdHeader_NewActivityStartedAndParentIdIsSet()
        {
            var rawEnvelope = new RawInboundEnvelope(
                new byte[5],
                new MessageHeaderCollection
            {
                {
                    DefaultMessageHeaders.TraceId,
                    "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"
                }
            },
                TestConsumerEndpoint.GetDefault(),
                TestConsumerEndpoint.GetDefault().Name,
                new TestOffset());

            var entered = false;

            await new ActivityConsumerBehavior(Substitute.For <IActivityEnricherFactory>())
            .HandleAsync(
                new ConsumerPipelineContext(
                    rawEnvelope,
                    Substitute.For <IConsumer>(),
                    Substitute.For <ISequenceStore>(),
                    Substitute.For <IServiceProvider>()),
                _ =>
            {
                Activity.Current.Should().NotBeNull();
                Activity.Current !.ParentId.Should()
                .Be("00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01");
                Activity.Current.Id.Should().StartWith("00-0af7651916cd43dd8448eb211c80319c");

                entered = true;

                return(Task.CompletedTask);
            });
예제 #18
0
        public async Task HandleError_WithPublish_MessagePublished()
        {
            var publisher       = Substitute.For <IPublisher>();
            var serviceProvider = new ServiceCollection().AddScoped(_ => publisher)
                                  .BuildServiceProvider(new ServiceProviderOptions {
                ValidateScopes = true
            });

            var policy = new TestErrorPolicy()
                         .Publish(_ => new TestEventTwo {
                Content = "aaa"
            })
                         .Build(serviceProvider);

            var envelope = new InboundEnvelope(
                new MemoryStream(),
                new[] { new MessageHeader(DefaultMessageHeaders.FailedAttempts, "3") },
                new TestOffset(),
                TestConsumerEndpoint.GetDefault(),
                TestConsumerEndpoint.GetDefault().Name);

            await policy.HandleErrorAsync(
                ConsumerPipelineContextHelper.CreateSubstitute(envelope, serviceProvider),
                new ArgumentNullException());

            await publisher.Received().PublishAsync(Arg.Any <TestEventTwo>());
        }
        public async Task HandleAsync_EndpointWithBinaryFileMessageSerializer_EnvelopeUntouched()
        {
            var rawContent = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 };
            var headers    = new[]
            {
                new MessageHeader("x-message-type", typeof(BinaryFileMessage).AssemblyQualifiedName)
            };
            var endpoint = TestConsumerEndpoint.GetDefault();

            endpoint.Serializer = new BinaryFileMessageSerializer();
            var envelope = new RawInboundEnvelope(
                rawContent,
                headers,
                endpoint,
                "test",
                new TestOffset());

            IRawInboundEnvelope?result = null;

            await new BinaryFileHandlerConsumerBehavior().HandleAsync(
                new ConsumerPipelineContext(
                    envelope,
                    Substitute.For <IConsumer>(),
                    Substitute.For <ISequenceStore>(),
                    Substitute.For <IServiceProvider>()),
                context =>
            {
                result = context.Envelope;
                return(Task.CompletedTask);
            });

            result.Should().BeSameAs(envelope);
        }
예제 #20
0
        public void LogCreatingNewConsumer_Logged()
        {
            var expectedMessage = "Creating new consumer for endpoint 'test'.";

            _silverbackLogger.LogCreatingNewConsumer(TestConsumerEndpoint.GetDefault());

            _loggerSubstitute.Received(LogLevel.Information, null, expectedMessage, 1015);
        }
        public void Constructor_NullRawMessage_NoExceptionIsThrown()
        {
            var envelope = new RawInboundEnvelope(
                (Stream?)null,
                null,
                TestConsumerEndpoint.GetDefault(),
                "test",
                new TestOffset("a", "b"));

            envelope.Should().NotBeNull();
        }
예제 #22
0
        public void LogConsumerLowLevelTrace_NoArguments_Logged()
        {
            var consumer = _serviceProvider.GetRequiredService <TestBroker>()
                           .AddConsumer(TestConsumerEndpoint.GetDefault());

            var expectedMessage = $"Message | consumerId: {consumer.Id}, endpointName: test";

            _silverbackLogger.LogConsumerLowLevelTrace(consumer, "Message");

            _loggerSubstitute.Received(LogLevel.Trace, null, expectedMessage, 1999);
        }
예제 #23
0
        public void LogConsumerStopError_Logged()
        {
            var consumer = _serviceProvider.GetRequiredService <TestBroker>()
                           .AddConsumer(TestConsumerEndpoint.GetDefault());

            var expectedMessage =
                "Error occurred while stopping the consumer. " +
                $"| consumerId: {consumer.Id}, endpointName: test";

            _silverbackLogger.LogConsumerStopError(consumer, new InvalidCastException());

            _loggerSubstitute.Received(LogLevel.Error, typeof(InvalidCastException), expectedMessage, 1130);
        }
예제 #24
0
        public void LogConsumerFatalError_Logged()
        {
            var consumer = _serviceProvider.GetRequiredService <TestBroker>()
                           .AddConsumer(TestConsumerEndpoint.GetDefault());

            var expectedMessage =
                "Fatal error occurred processing the consumed message. The consumer will be stopped. " +
                $"| consumerId: {consumer.Id}, endpointName: test";

            _silverbackLogger.LogConsumerFatalError(consumer, new AggregateException());

            _loggerSubstitute.Received(LogLevel.Critical, typeof(AggregateException), expectedMessage, 1023);
        }
예제 #25
0
        public void LogConsumerDisconnected_Logged()
        {
            var consumer = _serviceProvider.GetRequiredService <TestBroker>()
                           .AddConsumer(TestConsumerEndpoint.GetDefault());

            var expectedMessage =
                "Disconnected consumer from endpoint. " +
                $"| consumerId: {consumer.Id}, endpointName: test";

            _silverbackLogger.LogConsumerDisconnected(consumer);

            _loggerSubstitute.Received(LogLevel.Debug, null, expectedMessage, 1022);
        }
예제 #26
0
        public void AddConsumer_SomeEndpoint_ConsumerIsReturned()
        {
            var serviceProvider = ServiceProviderHelper.GetServiceProvider(
                services => services
                .AddSilverback()
                .WithConnectionToMessageBroker(
                    options => options
                    .AddBroker <TestBroker>()));

            var broker   = serviceProvider.GetRequiredService <IBroker>();
            var consumer = broker.AddConsumer(TestConsumerEndpoint.GetDefault());

            consumer.Should().NotBeNull();
        }
예제 #27
0
        public void AddConsumer_SameEndpoint_DifferentInstanceIsReturned()
        {
            var serviceProvider = ServiceProviderHelper.GetServiceProvider(
                services => services
                .AddSilverback()
                .WithConnectionToMessageBroker(
                    options => options
                    .AddBroker <TestBroker>()));

            var broker    = serviceProvider.GetRequiredService <IBroker>();
            var consumer  = broker.AddConsumer(TestConsumerEndpoint.GetDefault());
            var consumer2 = broker.AddConsumer(new TestConsumerEndpoint("test2"));

            consumer2.Should().NotBeSameAs(consumer);
        }
예제 #28
0
        public void LogErrorReconnectingConsumer_Logged()
        {
            var consumer = _serviceProvider.GetRequiredService <TestBroker>()
                           .AddConsumer(TestConsumerEndpoint.GetDefault());

            var expectedMessage =
                "Failed to reconnect the consumer. Will retry in 42000 milliseconds. | " +
                $"consumerId: {consumer.Id}, endpointName: test";

            _silverbackLogger.LogErrorReconnectingConsumer(
                TimeSpan.FromSeconds(42),
                consumer,
                new InvalidProgramException());

            _loggerSubstitute.Received(LogLevel.Warning, typeof(InvalidProgramException), expectedMessage, 1131);
        }
예제 #29
0
        public void LogConsumerLowLevelTrace_Logged()
        {
            var consumer = _serviceProvider.GetRequiredService <TestBroker>()
                           .AddConsumer(TestConsumerEndpoint.GetDefault());

            var expectedMessage =
                "Message A 42 True " +
                $"| consumerId: {consumer.Id}, endpointName: test";

            _silverbackLogger.LogConsumerLowLevelTrace(
                consumer,
                "Message {string} {int} {bool}",
                () => new object[] { "A", 42, true });

            _loggerSubstitute.Received(LogLevel.Trace, null, expectedMessage, 1999);
        }
        private static IRawInboundEnvelope GetEnvelope(Guid?messageId = null, IConsumerEndpoint?endpoint = null)
        {
            endpoint ??= TestConsumerEndpoint.GetDefault();

            var headers = new[]
            {
                new MessageHeader("x-message-id", messageId ?? Guid.NewGuid())
            };

            return(new RawInboundEnvelope(
                       Array.Empty <byte>(),
                       headers,
                       endpoint,
                       endpoint.Name,
                       new TestOffset()));
        }