public async Task RunAsync_should_execute_saga_runner()
        {
            var stateTypeResolver = NSubstitute.Substitute.For <ISagaTypeResolver>();
            var types             = (typeof(DummySaga), typeof(DummySagaState));

            stateTypeResolver.Resolve <StartDummySaga>()
            .Returns(types);

            var runner = NSubstitute.Substitute.For <ISagaRunner <DummySaga, DummySagaState> >();

            var sp = NSubstitute.Substitute.For <IServiceProvider>();

            sp.GetService(typeof(ISagaRunner <DummySaga, DummySagaState>))
            .Returns(runner);

            var typesCache = NSubstitute.Substitute.For <ITypesCache>();

            typesCache.GetGeneric(typeof(ISagaRunner <,>), typeof(DummySaga), typeof(DummySagaState))
            .Returns(typeof(ISagaRunner <DummySaga, DummySagaState>));

            var sut = new SagasRunner(sp, stateTypeResolver, typesCache);

            var message        = StartDummySaga.New();
            var messageContext = NSubstitute.Substitute.For <IMessageContext <StartDummySaga> >();

            messageContext.Message.Returns(message);

            await sut.RunAsync(messageContext);

            await runner.Received(1)
            .RunAsync(messageContext, Arg.Any <CancellationToken>());
        }
        public async Task RunAsync_should_throw_SagaNotFoundException_if_no_saga_registered_on_DI()
        {
            var stateTypeResolver = NSubstitute.Substitute.For <ISagaTypeResolver>();
            var types             = (typeof(DummySaga), typeof(DummySagaState));

            stateTypeResolver.Resolve <StartDummySaga>()
            .Returns(types);

            var sp = NSubstitute.Substitute.For <IServiceProvider>();

            var typesCache = NSubstitute.Substitute.For <ITypesCache>();

            typesCache.GetGeneric(typeof(ISagaRunner <,>), typeof(DummySaga), typeof(DummySagaState))
            .Returns(typeof(ISagaRunner <DummySaga, DummySagaState>));

            var sut = new SagasRunner(sp, stateTypeResolver, typesCache);

            var message        = StartDummySaga.New();
            var messageContext = NSubstitute.Substitute.For <IMessageContext <StartDummySaga> >();

            messageContext.Message.Returns(message);

            var ex = await Assert.ThrowsAsync <SagaNotFoundException>(() => sut.RunAsync(messageContext));

            ex.Message.Should().Contain("no saga registered on DI for message of type");
        }
        public async Task RunAsync_should_throw_if_message_null()
        {
            var typesCache        = NSubstitute.Substitute.For <ITypesCache>();
            var stateTypeResolver = NSubstitute.Substitute.For <ISagaTypeResolver>();
            var sp = NSubstitute.Substitute.For <IServiceScopeFactory>();

            var sut = new SagasRunner(sp, stateTypeResolver, typesCache);

            await Assert.ThrowsAsync <ArgumentNullException>(() => sut.RunAsync <StartDummySaga>(null));
        }
        public async Task RunAsync_should_do_nothing_when_no_runners_available()
        {
            var typesCache        = NSubstitute.Substitute.For <ITypesCache>();
            var stateTypeResolver = NSubstitute.Substitute.For <ISagaTypeResolver>();
            var sp = NSubstitute.Substitute.For <IServiceScopeFactory>();

            var sut = new SagasRunner(sp, stateTypeResolver, typesCache);

            var messageContext = NSubstitute.Substitute.For <IMessageContext <StartDummySaga> >();
            var result         = sut.RunAsync <StartDummySaga>(messageContext);

            result.Should().Be(Task.CompletedTask);
        }
        public async Task RunAsync_should_throw_if_runner_fails()
        {
            var message        = StartDummySaga.New();
            var messageContext = NSubstitute.Substitute.For <IMessageContext <StartDummySaga> >();

            messageContext.Message.Returns(message);


            var stateTypeResolver = NSubstitute.Substitute.For <ISagaTypeResolver>();
            var types             = (typeof(DummySaga), typeof(DummySagaState));

            stateTypeResolver.Resolve <StartDummySaga>()
            .Returns(new[] { types });

            var expectedException = new Exception("whoops");
            var runner            = NSubstitute.Substitute.For <ISagaRunner <DummySaga, DummySagaState> >();

            runner.When(r => r.RunAsync(messageContext, Arg.Any <CancellationToken>()))
            .Throw(expectedException);

            var sp = NSubstitute.Substitute.For <IServiceProvider>();

            sp.GetService(typeof(ISagaRunner <DummySaga, DummySagaState>))
            .Returns(runner);

            var scope = NSubstitute.Substitute.For <IServiceScope>();

            scope.ServiceProvider.Returns(sp);

            var scopeFactory = NSubstitute.Substitute.For <IServiceScopeFactory>();

            scopeFactory.CreateScope().Returns(scope);

            var typesCache = NSubstitute.Substitute.For <ITypesCache>();

            typesCache.GetGeneric(typeof(ISagaRunner <,>), typeof(DummySaga), typeof(DummySagaState))
            .Returns(typeof(ISagaRunner <DummySaga, DummySagaState>));

            var sut = new SagasRunner(scopeFactory, stateTypeResolver, typesCache);

            var ex = await Assert.ThrowsAsync <AggregateException>(() => sut.RunAsync(messageContext));

            ex.Message.Should().Contain("an error has occurred");
            ex.InnerExceptions.Should().NotBeNullOrEmpty()
            .And.HaveCount(1)
            .And.Contain(e => e.Message == expectedException.Message);
        }
        public async Task RunAsync_should_throw_if_no_saga_registered()
        {
            var typesCache        = NSubstitute.Substitute.For <ITypesCache>();
            var stateTypeResolver = NSubstitute.Substitute.For <ISagaTypeResolver>();
            var sp = NSubstitute.Substitute.For <IServiceProvider>();

            var sut = new SagasRunner(sp, stateTypeResolver, typesCache);

            var message        = StartDummySaga.New();
            var messageContext = NSubstitute.Substitute.For <IMessageContext <StartDummySaga> >();

            messageContext.Message.Returns(message);

            var ex = await Assert.ThrowsAsync <SagaNotFoundException>(() => sut.RunAsync(messageContext));

            ex.Message.Should().Contain("no saga registered for message of type");
        }