public void commands_should_not_deadlock()
        {
            var  bus     = new CommandBus("local");
            long gotCmd1 = 0;
            long gotCmd2 = 0;

            bus.Subscribe(new AdHocCommandHandler <TestCommands.TestCommand>(
                              cmd =>
            {
                Interlocked.Exchange(ref gotCmd1, 1);
                bus.TryFire(new TestCommands.TestCommand2(Guid.NewGuid(), null));
                return(true);
            }));
            bus.Subscribe(new AdHocCommandHandler <TestCommands.TestCommand2>(
                              cmd =>
            {
                Interlocked.Exchange(ref gotCmd2, 1);
                return(true);
            }));
            CommandResponse result;

            bus.TryFire(new TestCommands.TestCommand(Guid.NewGuid(), null), out result);

            Assert.True(result is Success, $"Got Fail: {(result as Fail)?.Exception.Message}");

            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref gotCmd1) == 1, msg: "Expected Cmd1 handled");
            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref gotCmd2) == 1, msg: "Expected Cmd2 handled");
        }
        public void concurrent_commands_should_pass()
        {
            var  bus        = new CommandBus("local", slowCmdThreshold: TimeSpan.FromMilliseconds(50));
            long gotSuccess = 0;
            long gotFail    = 0;
            long gotTimeout = 0;
            Func <Command, bool> testAction        = cmd => { Interlocked.Increment(ref gotSuccess); return(true); };
            Func <Command, bool> testFailAction    = cmd => { Interlocked.Increment(ref gotFail); return(true); };
            Func <Command, bool> testTimeoutAction = cmd =>
            {
                Interlocked.Increment(ref gotTimeout);
                Thread.Sleep(55); //timeout == 50 ms
                return(true);
            };

            Parallel.Invoke(
                () => bus.Subscribe(new AdHocCommandHandler <TestCommands.TestCommand>(testAction)),
                () => bus.Subscribe(new AdHocCommandHandler <TestCommands.TestCommand2>(testTimeoutAction)),
                () => bus.Subscribe(new AdHocCommandHandler <TestCommands.TestCommand3>(testFailAction))
                );
            Assert.True(gotSuccess == 0, $"gotSuccess should be 0, found { gotSuccess}");
            for (int i = 0; i < 50; i++)
            {
                Parallel.Invoke(
                    () => bus.Fire(new TestCommands.TestCommand(Guid.NewGuid(), null)),
                    () => bus.TryFire(new TestCommands.TestCommand2(Guid.NewGuid(), null)),
                    () => bus.TryFire(new TestCommands.TestCommand3(Guid.NewGuid(), null)));
            }

            Assert.True(gotSuccess == 50, $"gotSuccess should be 50, found { gotSuccess}");
            Assert.True(gotFail == 50, $"gotFail should be 50, found { gotFail}");
            Assert.True(gotTimeout == 50, $"gotTimeout should be 50, found { gotTimeout}");
        }
        public void multiple_commands_can_register()
        {
            var  bus     = new CommandBus("local");
            long gotCmd1 = 0;
            long gotCmd2 = 0;

            bus.Subscribe(new AdHocCommandHandler <TestCommands.TestCommand>(
                              cmd =>
            {
                Interlocked.Exchange(ref gotCmd1, 1);
                return(true);
            }));
            bus.Subscribe(new AdHocCommandHandler <TestCommands.TestCommand2>(
                              cmd =>
            {
                Interlocked.Exchange(ref gotCmd2, 1);
                return(true);
            }));
            CommandResponse result;

            bus.TryFire(new TestCommands.TestCommand(Guid.NewGuid(), null), out result);
            Assert.True(result is Success);
            bus.TryFire(new TestCommands.TestCommand2(Guid.NewGuid(), null), out result);
            Assert.True(result is Success);
            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref gotCmd1) == 1, msg: "Expected Cmd1 handled");
            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref gotCmd2) == 1, msg: "Expected Cmd2 handled");
        }
        public void tryfire_commands_should_not_call_other_commands()
        {
            var  bus    = new CommandBus("local");
            long gotCmd = 0;


            bus.Subscribe(new AdHocCommandHandler <TestCommands.TestCommand>(
                              cmd =>
            {
                Interlocked.Increment(ref gotCmd);
                return(true);
            }));
            bus.Subscribe(new AdHocCommandHandler <TestCommands.TestCommand2>(
                              cmd =>
            {
                Interlocked.Increment(ref gotCmd);
                return(true);
            }));
            CommandResponse response;

            var passed = bus.TryFire(
                new TestCommands.TestCommand(Guid.NewGuid(), null), out response, TimeSpan.FromMilliseconds(1500));

            Assert.True(passed, "Expected false return");
            Thread.Sleep(100); //let other threads complete
            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref gotCmd) == 1,
                                   msg: "Expected command received once, got " + gotCmd);
            Assert.IsType(typeof(Success), response);
        }
        public void typed_failing_tryfire_command_should_fail()
        {
            var          bus          = new CommandBus("local");
            const int    data         = 12356;
            const string errText      = @"I knew this would happen.";
            long         gotResponse  = 0;
            long         responseData = 0;

            bus.Subscribe(new AdHocTypedCommandHandler <TestCommands.TypedTestCommand, TestCommands.TestFailedCommandResponse>(
                              cmd => new TestCommands.TestFailedCommandResponse(cmd, new CommandException(errText, cmd), data)));
            bus.Subscribe(new AdHocHandler <TestCommands.TestFailedCommandResponse>(
                              cmd =>
            {
                Interlocked.Exchange(ref gotResponse, 1);
                Interlocked.Exchange(ref responseData, cmd.Data);
            }
                              ));

            CommandResponse response;
            var             command = new TestCommands.TypedTestCommand(Guid.NewGuid(), null);
            var             passed  = bus.TryFire(command, out response);

            Assert.False(passed, "Expected false return");
            Assert.IsType(typeof(TestCommands.TestFailedCommandResponse), response);
            Assert.IsType(typeof(CommandException), ((Fail)response).Exception);
            Assert.Equal($"{command.GetType().Name}: {errText}", ((Fail)response).Exception.Message);
            Assert.Equal(data, ((TestCommands.TestFailedCommandResponse)response).Data);
            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref gotResponse) == 1, null, "Expected Fail");
            Assert.IsOrBecomesTrue(() => data == responseData);
        }
        public void typed_tryfire_passing_command_should_pass()
        {
            var       bus  = new CommandBus("local");
            const int data = 12356;

            long gotResponse  = 0;
            long responseData = 0;

            bus.Subscribe(new AdHocTypedCommandHandler <TestCommands.TypedTestCommand, TestCommands.TestCommandResponse>(
                              cmd => new TestCommands.TestCommandResponse(cmd, data)));
            bus.Subscribe(new AdHocHandler <TestCommands.TestCommandResponse>(
                              cmd =>
            {
                Interlocked.Exchange(ref gotResponse, 1);
                Interlocked.Exchange(ref responseData, cmd.Data);
            }
                              ));

            CommandResponse response;
            var             pass = bus.TryFire(new TestCommands.TypedTestCommand(Guid.NewGuid(), null), out response);

            Assert.True(pass, "Expected true return value");
            Assert.IsType(typeof(TestCommands.TestCommandResponse), response);
            Assert.Equal(data, ((TestCommands.TestCommandResponse)response).Data);
            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref gotResponse) == 1, null, "Expected Success");
            Assert.IsOrBecomesTrue(() => data == responseData);
        }
        public void tryfire_slow_commands_should_return_timeout()
        {
            var  bus            = new CommandBus("local");
            long gotCmd1        = 0;
            long cancelRecieved = 0;

            bus.Subscribe(new AdHocCommandHandler <TestCommands.TestCommand>(
                              cmd =>
            {
                Interlocked.Exchange(ref gotCmd1, 1);
                Task.Delay(1000).Wait();
                return(true);
            },
                              (cancel, currentCmdId) =>
            {
                if (cancel.CommandType == typeof(TestCommands.TestCommand) &&
                    (currentCmdId == cancel.CommandId))
                {
                    Interlocked.Increment(ref cancelRecieved);
                }
                return(Unit.Default);
            }));

            CommandResponse response;
            var             passed = bus.TryFire(new TestCommands.TestCommand(Guid.NewGuid(), null), out response);

            Assert.False(passed, "Expected false return");
            Assert.IsType(typeof(Fail), response);
            Assert.IsType(typeof(CommandTimedOutException), ((Fail)response).Exception);
            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref gotCmd1) == 1, msg: "Expected Cmd1 handled");
            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref cancelRecieved) == 1, msg: "Expected cancel received");
        }
        public void tryfire_unsubscribed_commands_should_return_throw_commandNotHandledException()
        {
            var             bus = new CommandBus("local");
            CommandResponse response;
            var             passed = bus.TryFire(new TestCommands.TestCommand(Guid.NewGuid(), null), out response);

            Assert.False(passed, "Expected false return");
            Assert.IsType(typeof(Fail), response);
            Assert.IsType(typeof(CommandNotHandledException), ((Fail)response).Exception);
        }
        public void tryfire_failing_command_should_fail()
        {
            var  bus     = new CommandBus("local");
            long gotFail = 0;

            bus.Subscribe(new AdHocCommandHandler <TestCommands.TestCommand>(cmd => false));
            bus.Subscribe(new AdHocHandler <Fail>(cmd => Interlocked.Exchange(ref gotFail, 1)));
            CommandResponse response;
            var             pass = bus.TryFire(new TestCommands.TestCommand(Guid.NewGuid(), null), out response);

            Assert.False(pass, "Expected false return value");
            Assert.IsType(typeof(Fail), response);
            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref gotFail) == 1, null, "Expected Fail");
        }
        public void tryfire_passing_command_should_pass()
        {
            var  bus        = new CommandBus("local");
            long gotSuccess = 0;

            bus.Subscribe(new AdHocCommandHandler <TestCommands.TestCommand>(cmd => true));
            bus.Subscribe(new AdHocHandler <Success>(cmd => Interlocked.Exchange(ref gotSuccess, 1)));
            CommandResponse response;
            var             pass = bus.TryFire(new TestCommands.TestCommand(Guid.NewGuid(), null), out response);

            Assert.True(pass, "Expected true return value");
            Assert.IsType(typeof(Success), response);
            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref gotSuccess) == 1, null, "Expected Success");
        }
        public void tryfire_unsubscribed_commands_should_return_ack_timeout()
        {
            var  bus             = new CommandBus("local");
            long cancelPublished = 0;

            bus.Subscribe(new AdHocHandler <CancelCommand>(c => Interlocked.Increment(ref cancelPublished)));
            CommandResponse response;
            var             passed = bus.TryFire(new TestCommands.TestCommand(Guid.NewGuid(), null), out response);

            Assert.False(passed, "Expected false return");
            Assert.IsType(typeof(Fail), response);
            Assert.IsType(typeof(CommandNotHandledException), ((Fail)response).Exception);
            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref cancelPublished) == 1, msg: "Expected cancel published");
        }
        public void handlers_that_wrap_exceptions_return_on_tryfire()
        {
            var bus = new CommandBus("local");

            long         gotCmd      = 0;
            long         gotResponse = 0;
            long         gotAck      = 0;
            long         msgCount    = 0;
            const string errText     = @"I knew this would happen.";

            bus.Subscribe(new AdHocCommandHandler <TestCommands.TestCommand>(
                              cmd =>
            {
                Interlocked.Exchange(ref gotCmd, 1);
                throw new CommandException(errText, cmd);
            },
                              // ReSharper disable once RedundantArgumentDefaultValue
                              wrapExceptions: true));

            bus.Subscribe(new AdHocHandler <CommandResponse>(
                              cmd => Interlocked.Exchange(ref gotResponse, 1)));

            bus.Subscribe(new AdHocHandler <AckCommand>(
                              cmd => Interlocked.Exchange(ref gotAck, 1)));

            bus.Subscribe(new AdHocHandler <Message>(
                              cmd => Interlocked.Increment(ref msgCount)));

            CommandResponse response;
            var             command = new TestCommands.TestCommand(Guid.NewGuid(), null);
            var             passed  = bus.TryFire(command, out response);

            Assert.False(passed, "Expected false return");
            Assert.IsType(typeof(Fail), response);
            Assert.IsType(typeof(CommandException), ((Fail)response).Exception);

            Assert.True(string.Equals($"{command.GetType().Name}: {errText}", ((Fail)response).Exception.Message, StringComparison.Ordinal));


            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref msgCount) == 3, null, "Expected 3 Messages");
            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref gotAck) == 1, null, "Expected Ack");
            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref gotCmd) == 1, null, "Expected Command was handled");
            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref gotResponse) == 1, null, "Expected Response");
        }
        public void tryfire_slow_commands_should_return_timeout()
        {
            var  bus     = new CommandBus("local");
            long gotCmd1 = 0;

            bus.Subscribe(new AdHocCommandHandler <TestCommands.TestCommand>(
                              cmd =>
            {
                Interlocked.Exchange(ref gotCmd1, 1);
                Task.Delay(1000).Wait();
                return(true);
            }));

            CommandResponse response;
            var             passed = bus.TryFire(new TestCommands.TestCommand(Guid.NewGuid(), null), out response);

            Assert.False(passed, "Expected false return");
            Assert.IsType(typeof(Fail), response);//,$"Expected 'Fail' got {response.GetType().Name}");
            Assert.IsType(typeof(CommandTimedOutException), ((Fail)response).Exception);
            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref gotCmd1) == 1, msg: "Expected Cmd1 handled");
        }
        public void passing_commands_on_connected_busses_should_pass()
        {
            var  bus     = new CommandBus("local");
            var  bus2    = new CommandBus("remote");
            var  conn    = new BusConnector(bus, bus2);
            long gotCmd1 = 0;

            bus2.Subscribe(new AdHocCommandHandler <TestCommands.TestCommand>(
                               cmd =>
            {
                Interlocked.Exchange(ref gotCmd1, 1);
                return(true);
            }));

            CommandResponse result;

            bus.TryFire(new TestCommands.TestCommand(Guid.NewGuid(), null), out result);
            Assert.True(result is Success);

            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref gotCmd1) == 1, msg: "Expected Cmd1 handled");
        }
        public void tryfire_oversubscribed_commands_should_return_false()
        {
            var  bus             = new CommandBus("local");
            var  bus2            = new CommandBus("remote");
            var  conn            = new BusConnector(bus, bus2);
            long gotCmd          = 0;
            long proccessedCmd   = 0;
            long cancelPublished = 0;
            long cancelReturned  = 0;

            bus.Subscribe(new AdHocCommandHandler <TestCommands.TestCommand>(
                              cmd =>
            {
                Interlocked.Increment(ref gotCmd);
                Task.Delay(250).Wait();
                Interlocked.Increment(ref proccessedCmd);
                return(true);
            }));
            bus2.Subscribe(new AdHocCommandHandler <TestCommands.TestCommand>(
                               cmd =>
            {
                Interlocked.Increment(ref gotCmd);
                Task.Delay(250).Wait();
                Interlocked.Increment(ref proccessedCmd);
                return(true);
            }));
            bus.Subscribe(new AdHocCommandHandler <TestCommands.TestCommand2>(
                              cmd =>
            {
                Interlocked.Increment(ref gotCmd);
                Task.Delay(250).Wait();
                Interlocked.Increment(ref proccessedCmd);
                return(true);
            }));
            bus2.Subscribe(new AdHocHandler <Canceled>(c => Interlocked.Increment(ref cancelPublished)));
            bus2.Subscribe(new AdHocHandler <Fail>(
                               c =>
            {
                if (c.Exception is CommandCanceledException)
                {
                    Interlocked.Increment(ref cancelReturned);
                }
            }));
            CommandResponse response;
            var             timer  = Stopwatch.StartNew();
            var             passed = bus.TryFire(
                new TestCommands.TestCommand(Guid.NewGuid(), null), out response, TimeSpan.FromMilliseconds(1500));

            timer.Stop();
            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref gotCmd) <= 1,
                                   msg: "Expected command received no more than once, got " + gotCmd);
            Assert.True(timer.ElapsedMilliseconds < 1000, "Expected failure before task completion.");
            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref proccessedCmd) == 0,
                                   msg: "Expected command failed before handled; got " + proccessedCmd);

            Assert.False(passed, "Expected false return");
            Assert.IsType(typeof(Fail), response);
            Assert.IsType(typeof(CommandOversubscribedException), ((Fail)response).Exception);
            Assert.IsOrBecomesTrue(() => Interlocked.Read(ref proccessedCmd) <= 1, 1500, msg: "Expected command handled no more than once, actual" + proccessedCmd);
            Assert.IsOrBecomesTrue(
                () => Interlocked.Read(ref cancelPublished) == 1,
                msg: "Expected cancel published once");

            Assert.IsOrBecomesTrue(
                () => Interlocked.Read(ref cancelReturned) == 1,
                msg: "Expected cancel returned once, actual " + cancelReturned);
        }