public async Task CanHandleMessageAndSendOutgoingMessagesEvenWhenTransportIsFlaky()
    {
        using var gotSomeMessage    = new ManualResetEvent(initialState: false);
        using var gotAnotherMessage = new ManualResetEvent(initialState: false);

        var flakySenderTransportDecoratorSettings = new FlakySenderTransportDecoratorSettings();

        async Task HandlerFunction(IBus bus, IMessageContext context, SomeMessage message)
        {
            await bus.Advanced.Routing.Send("secondConsumer", new AnotherMessage());

            gotSomeMessage.Set();
        }

        using var firstConsumer  = CreateConsumer("firstConsumer", activator => activator.Handle <SomeMessage>(HandlerFunction), flakySenderTransportDecoratorSettings);
        using var secondConsumer = CreateConsumer("secondConsumer", activator => activator.Handle <AnotherMessage>(async _ => gotAnotherMessage.Set()));

        using var client = CreateOneWayClient(router => router.TypeBased().Map <SomeMessage>("firstConsumer"));

        // make it so that the first consumer cannot send
        flakySenderTransportDecoratorSettings.SuccessRate = 0;

        await client.Send(new SomeMessage());

        // wait for SomeMessage to be handled
        gotSomeMessage.WaitOrDie(timeout: TimeSpan.FromSeconds(3));

        // now make it possible for first consumer to send again
        flakySenderTransportDecoratorSettings.SuccessRate = 1;

        // wait for AnotherMessage to arrive
        gotAnotherMessage.WaitOrDie(timeout: TimeSpan.FromSeconds(15));
    }
    IBus CreateOneWayClient(Action <StandardConfigurer <IRouter> > routing = null, FlakySenderTransportDecoratorSettings flakySenderTransportDecoratorSettings = null)
    {
        return(Configure.With(new BuiltinHandlerActivator())
               .Transport(t =>
        {
            t.UseInMemoryTransportAsOneWayClient(_network);

            if (flakySenderTransportDecoratorSettings != null)
            {
                t.Decorate(c => new FlakySenderTransportDecorator(c.Get <ITransport>(),
                                                                  flakySenderTransportDecoratorSettings));
            }
        })
               .Routing(r => routing?.Invoke(r))
               .Subscriptions(s => s.StoreInMemory(_subscriberStore))
               .Outbox(o => o.StoreInSqlServer(ConnectionString, "RebusOutbox"))
               .Start());
    }
    public async Task CanUseOutboxOutsideOfRebusHandler_Publish(bool commitTransaction, bool expectMessageToBeReceived)
    {
        var settings = new FlakySenderTransportDecoratorSettings();

        using var messageWasReceived = new ManualResetEvent(initialState: false);
        using var server             = CreateConsumer("server", a => a.Handle <SomeMessage>(async _ => messageWasReceived.Set()));

        await server.Subscribe <SomeMessage>();

        using var client = CreateOneWayClient(flakySenderTransportDecoratorSettings: settings);

        // set success rate pretty low, so we're sure that it's currently not possible to use the
        // real transport - this is a job for the outbox!
        settings.SuccessRate = 0;

        // pretending we're in a web app - we have these two bad boys at work:
        await using (var connection = new SqlConnection(ConnectionString))
        {
            await connection.OpenAsync();

            await using var transaction = connection.BeginTransaction();

            // this is how we would use the outbox for outgoing messages
            using var scope = new RebusTransactionScope();
            scope.UseOutbox(connection, transaction);
            await client.Publish(new SomeMessage());

            await scope.CompleteAsync();

            if (commitTransaction)
            {
                // this is what we were all waiting for!
                await transaction.CommitAsync();
            }
        }

        // we would not have gotten this far without the outbox - now let's pretend that the transport has recovered
        settings.SuccessRate = 1;

        // wait for server to receive the event
        Assert.That(messageWasReceived.WaitOne(TimeSpan.FromSeconds(15)), Is.EqualTo(expectMessageToBeReceived),
                    $"When commitTransaction={commitTransaction} we {(expectMessageToBeReceived ? "expected the message to be sent and thus received" : "did NOT expect the message to be sent and therefore also not received")}");
    }
    IBus CreateConsumer(string queueName, Action <BuiltinHandlerActivator> handlers = null, FlakySenderTransportDecoratorSettings flakySenderTransportDecoratorSettings = null)
    {
        var activator = new BuiltinHandlerActivator();

        handlers?.Invoke(activator);

        Configure.With(activator)
        .Transport(t =>
        {
            t.UseInMemoryTransport(_network, queueName);

            if (flakySenderTransportDecoratorSettings != null)
            {
                t.Decorate(c => new FlakySenderTransportDecorator(c.Get <ITransport>(),
                                                                  flakySenderTransportDecoratorSettings));
            }
        })
        .Subscriptions(s => s.StoreInMemory(_subscriberStore))
        .Outbox(o => o.StoreInSqlServer(ConnectionString, "RebusOutbox"))
        .Start();

        return(activator.Bus);
    }
 public FlakySenderTransportDecorator(ITransport transport,
                                      FlakySenderTransportDecoratorSettings flakySenderTransportDecoratorSettings)
 {
     _transport = transport;
     _flakySenderTransportDecoratorSettings = flakySenderTransportDecoratorSettings;
 }