public async Task AddTransactionAsync_TimeoutAndFailToCallback_ShouldNotThrow()
        {
            using (var elapsed = new ManualResetEventSlim())
            {
                // Arrange.
                await this.Initialize(CancellationToken.None);

                var builder = new WatchArgsBuilder(this.callbackRepository);
                builder.Timeout = TimeSpan.FromMilliseconds(200);
                this.ruleRepository.When(r => r.UpdateStatusAsync(Arg.Any <Guid>(), Arg.Any <RuleStatus>(), Arg.Any <CancellationToken>()))
                .Throw(info => {
                    elapsed.Set();
                    return(new Exception());
                });

                // Act.
                await builder.Call(this.subject.AddTransactionAsync);

                elapsed.Wait(1500);

                // Assert.
                _ = this.callbackExecuter
                    .Received(0)
                    .ExecuteAsync
                    (
                    Arg.Any <Guid>(),
                    Arg.Any <Uri>(),
                    Arg.Any <CallbackResult>(),
                    Arg.Any <CancellationToken>()
                    );
            }
        }
        public async Task AddTransactionAsync_AndPushNoEvent_ShouldTimeout()
        {
            using (var elapsed = new ManualResetEventSlim())
            {
                // Arrange.
                await this.Initialize(CancellationToken.None);

                var builder = new WatchArgsBuilder(this.callbackRepository);
                builder.Timeout = TimeSpan.FromMilliseconds(200);
                this.callbackExecuter.When(e => e.ExecuteAsync(Arg.Any <Guid>(), Arg.Any <Uri>(), Arg.Any <CallbackResult>(), Arg.Any <CancellationToken>()))
                .Do(c => elapsed.Set());

                // Act.
                await builder.Call(this.subject.AddTransactionAsync);

                elapsed.Wait(1500);

                // Assert.
                _ = this.callbackExecuter
                    .Received(1)
                    .ExecuteAsync
                    (
                    Arg.Any <Guid>(),
                    Arg.Any <Uri>(),
                    Arg.Is <CallbackResult>(r => r.Status == CallbackResult.StatusError),
                    Arg.Any <CancellationToken>()
                    );
            }
        }
        public async Task AddTransactionAsync_AndWaitSomeWatchesToTimeout_ShouldCallExecute()
        {
            // Arrange.
            var builder = new WatchArgsBuilder(this.callbackRepository);

            // Act.
            builder.Timeout = TimeSpan.FromSeconds(1);
            await builder.Call(this.subject.AddTransactionAsync);

            builder.Timeout = TimeSpan.FromSeconds(2);
            await builder.Call(this.subject.AddTransactionAsync);

            builder.Timeout = TimeSpan.FromDays(1);
            await builder.Call(this.subject.AddTransactionAsync);

            Thread.Sleep(TimeSpan.FromSeconds(3));

            // Assert.
            _ = this.callbackExecuter.Received(2).ExecuteAsync
                (
                Arg.Any <Guid>(),
                this.defaultUrl,
                Arg.Is <CallbackResult>
                (
                    result => result == builder.TimeoutData
                ),
                Arg.Any <CancellationToken>()
                );
        }
        public async Task AddTransactionAsync_WithOnChainTransactionAndFork_ShouldBeResumeAndTimeout()
        {
            // Arrange.
            var transaction = Transaction.Parse(TestTransaction.Raw1, ZcoinNetworks.Instance.Regtest);
            var builder     = new WatchArgsBuilder(this.callbackRepository);

            builder.Transaction = transaction.GetHash();

            this.blockStorage
            .GetTransactionAsync(transaction.GetHash(), Arg.Any <CancellationToken>())
            .Returns(transaction);

            var(block, _) = GenerateBlock();

            builder.Timeout = TimeSpan.FromMilliseconds(500);
            await builder.Call(this.subject.AddTransactionAsync);

            // Act.
            await this.blockListener.BlockRemovingAsync(block, 1, CancellationToken.None);

            Thread.Sleep(TimeSpan.FromMilliseconds(1000));

            // Assert.
            _ = this.callbackExecuter.Received(1).ExecuteAsync
                (
                Arg.Any <Guid>(),
                this.defaultUrl,
                Arg.Is <CallbackResult>
                (
                    result => result == builder.TimeoutData
                ),
                Arg.Any <CancellationToken>()
                );
        }
        public async Task ConfirmationUpdateAsync_AndReachRequiredConfirmations_ShouldCallSuccess()
        {
            // Arrange.
            var builder = new WatchArgsBuilder(this.callbackRepository);

            builder.Timeout       = TimeSpan.FromSeconds(2);
            builder.Confirmations = 10;
            var rule = await builder.Call(this.subject.AddTransactionAsync);

            var watches = new List <TransactionWatch <Rule> >()
            {
                new TransactionWatch <Rule>(rule, uint256.Zero, builder.Transaction),
            };

            await this.handler.AddWatchesAsync(watches, CancellationToken.None);

            // Act.
            for (var confirmation = 1; confirmation <= builder.Confirmations; confirmation++)
            {
                await this.handler.ConfirmationUpdateAsync(watches[0], confirmation, ConfirmationType.Confirmed, CancellationToken.None);
            }
            Thread.Sleep(TimeSpan.FromSeconds(2));

            // Assert.
            _ = this.callbackExecuter.Received(1).ExecuteAsync
                (
                rule.Callback.Id,
                rule.Callback.Url,
                Arg.Is <CallbackResult>
                (
                    result => result.Status == CallbackResult.StatusSuccess
                ),
                Arg.Any <CancellationToken>()
                );
        }
        public async Task AddTransactionAsync_AndSuccessToExecuteCallback_CompletedFlagAndHistorySuccessFlagShouldBeSet()
        {
            // Arrange.
            var builder = new WatchArgsBuilder(this.callbackRepository);

            builder.Timeout = TimeSpan.FromMilliseconds(500);

            // Act.
            var rule = await builder.Call(this.subject.AddTransactionAsync);

            Thread.Sleep(TimeSpan.FromMilliseconds(1000));

            // Assert.
            _ = this.ruleRepository.Received(1).UpdateStatusAsync(rule.Id, Arg.Any <RuleStatus>(), Arg.Any <CancellationToken>());

            _ = this.callbackExecuter.Received(1).ExecuteAsync
                (
                Arg.Any <Guid>(),
                Arg.Any <Uri>(),
                Arg.Any <CallbackResult>(),
                Arg.Any <CancellationToken>()
                );

            _ = this.callbackRepository.Received(1).SetCompletedAsyc
                (
                Arg.Any <Guid>(),
                Arg.Any <CancellationToken>()
                );
        }
        public async Task AddWatchesAsync_WithValidArgs_ShouldNotThrow()
        {
            // Arrange.
            var builder = new WatchArgsBuilder(this.callbackRepository);
            var watch1  = await builder.Call(this.subject.AddTransactionAsync);

            var watch2 = await builder.Call(this.subject.AddTransactionAsync);

            var watches = new List <TransactionWatch <Rule> >()
            {
                new TransactionWatch <Rule>(watch1, uint256.Zero, uint256.Zero),
                new TransactionWatch <Rule>(watch2, uint256.Zero, uint256.Zero),
            };

            // Act.
            await this.handler.AddWatchesAsync(watches, CancellationToken.None);

            _ = this.ruleRepository.Received(1).UpdateCurrentWatchAsync
                (
                Arg.Is <Guid>(id => id == watch1.Id),
                Arg.Is <Guid>(id => id == watches[0].Id),
                Arg.Any <CancellationToken>()
                );

            _ = this.ruleRepository.Received(1).UpdateCurrentWatchAsync
                (
                Arg.Is <Guid>(id => id == watch2.Id),
                Arg.Is <Guid>(id => id == watches[1].Id),
                Arg.Any <CancellationToken>()
                );
        }
        public async Task AddTransactionAsync_WithValidArgument_ShouldSuccess()
        {
            // Arrange.
            var builder = new WatchArgsBuilder(this.callbackRepository);

            builder.Timeout = TimeSpan.FromMilliseconds(500);

            // Act.
            var rule = await builder.Call(this.subject.AddTransactionAsync);

            Thread.Sleep(TimeSpan.FromMilliseconds(1000));

            // Assert.
            _ = this.callbackRepository.Received(1).AddAsync
                (
                Arg.Is <IPAddress>(ip => ip == builder.Ip),
                Arg.Is <Uri>(url => url == builder.CallbackUrl),
                Arg.Any <CancellationToken>()
                );

            _ = this.ruleRepository.Received(1).AddAsync
                (
                Arg.Is <uint256>(tx => tx == builder.Transaction),
                Arg.Is <int>(confirmations => confirmations == builder.Confirmations),
                Arg.Is <TimeSpan>(t => t == builder.Timeout),
                Arg.Is <CallbackResult>(r => r == builder.SuccessData),
                Arg.Is <CallbackResult>(r => r == builder.TimeoutData),
                Arg.Is <Callback>(c => c == rule.Callback),
                Arg.Any <CancellationToken>()
                );

            _ = this.callbackExecuter.Received(1).ExecuteAsync
                (
                Arg.Is <Guid>(id => id == rule.Callback.Id),
                this.defaultUrl,
                Arg.Is <CallbackResult>
                (
                    result => result == builder.TimeoutData
                ),
                Arg.Any <CancellationToken>()
                );

            _ = this.callbackRepository.Received(1).AddHistoryAsync
                (
                Arg.Is <Guid>(id => id == rule.Callback.Id),
                Arg.Is <CallbackResult>(r => r == rule.TimeoutResponse),
                Arg.Any <CancellationToken>()
                );

            _ = this.callbackRepository.Received(1).SetCompletedAsyc
                (
                Arg.Is <Guid>(id => id == rule.Callback.Id),
                Arg.Any <CancellationToken>()
                );
        }
        public async Task RemoveUncompletedWatchesAsync_WithRemainingTimeout_ShouldNotExecuteTimeoutImmediately()
        {
            // Arrange.
            var builder = new WatchArgsBuilder(this.callbackRepository);

            builder.Timeout       = TimeSpan.FromMilliseconds(500);
            builder.Confirmations = 10;
            var watch = await builder.Call(this.subject.AddTransactionAsync);

            var watches = new List <TransactionWatch <Rule> >()
            {
                new TransactionWatch <Rule>(watch, uint256.Zero, builder.Transaction),
            };

            await this.handler.AddWatchesAsync(watches, CancellationToken.None);

            Thread.Sleep(TimeSpan.FromMilliseconds(1000));

            // Act & Assert.
            var updated = await this.ruleRepository.GetAsync(watch.Id, CancellationToken.None);

            Assert.True(await this.ruleRepository.GetRemainingWaitingTimeAsync(updated.Id, CancellationToken.None) < TimeSpan.FromSeconds(1));

            await this.handler.RemoveUncompletedWatchesAsync(uint256.Zero, CancellationToken.None);

            _ = this.callbackExecuter.Received(0).ExecuteAsync
                (
                Arg.Any <Guid>(),
                Arg.Any <Uri>(),
                Arg.Any <CallbackResult>(),
                Arg.Any <CancellationToken>()
                );

            Thread.Sleep(TimeSpan.FromMilliseconds(1000));

            _ = this.callbackExecuter.Received(1).ExecuteAsync
                (
                Arg.Any <Guid>(),
                Arg.Any <Uri>(),
                Arg.Is <CallbackResult>
                (
                    result => result.Status == CallbackResult.StatusError
                ),
                Arg.Any <CancellationToken>()
                );

            _ = this.ruleRepository.Received(1).UpdateCurrentWatchAsync(
                Arg.Is <Guid>(id => id == watch.Id), Arg.Is <Guid?>(id => id == null), Arg.Any <CancellationToken>());
        }
        public async Task AddTransactionAsync_WithNullArguments_ShouldThrow()
        {
            var builder  = new WatchArgsBuilder(this.callbackRepository);
            var callback = await this.callbackRepository.AddAsync(
                builder.Ip,
                builder.CallbackUrl,
                CancellationToken.None);

            await Assert.ThrowsAsync <ArgumentOutOfRangeException>(
                () => this.subject.AddTransactionAsync(
                    uint256.One,
                    10,
                    TimeSpan.MinValue,
                    callback,
                    new CallbackResult("", ""),
                    new CallbackResult("", ""),
                    CancellationToken.None)
                );
        }
        public async Task AddTransactionAsync_AndPushUntilRequiredConfirmation_ShouldCallSuccess()
        {
            // Arrange.
            await this.Initialize(CancellationToken.None);

            var(block, _) = GenerateBlock();
            block.AddTransaction(Transaction.Create(ZcoinNetworks.Instance.Regtest));

            var builder = new WatchArgsBuilder(this.callbackRepository);

            builder.Confirmations = 10;
            builder.Timeout       = TimeSpan.FromMilliseconds(500);
            builder.Transaction   = block.Transactions[0].GetHash();

            var rule = await builder.Call(this.subject.AddTransactionAsync);

            // Act.
            await this.blockListener.BlockAddedAsync(block, 1, CancellationToken.None);

            for (var i = 0; i < builder.Confirmations - 1; i++)
            {
                int height;
                (block, height) = GenerateBlock();

                await this.blockListener.BlockAddedAsync(block, height, CancellationToken.None);
            }

            // Assert.
            _ = this.callbackExecuter
                .Received(1)
                .ExecuteAsync
                (
                rule.Callback.Id,
                rule.Callback.Url,
                Arg.Is <CallbackResult>
                (
                    r => r.Status == CallbackResult.StatusSuccess
                ),
                Arg.Any <CancellationToken>()
                );
        }
        public async Task AddTransactionAsync_AndPushBlockWhichContainTransaction_TimerShouldBeStopped()
        {
            // Arrange.
            await this.Initialize(CancellationToken.None);

            var(block, _) = GenerateBlock();
            block.AddTransaction(Transaction.Create(ZcoinNetworks.Instance.Regtest));

            var builder = new WatchArgsBuilder(this.callbackRepository);

            builder.Timeout     = TimeSpan.FromSeconds(1);
            builder.Transaction = block.Transactions[0].GetHash();

            await builder.Call(this.subject.AddTransactionAsync);

            // Act.
            await this.blockListener.BlockAddedAsync(block, 1, CancellationToken.None);

            Thread.Sleep(TimeSpan.FromSeconds(1));

            // Assert.
            _ = this.callbackExecuter.Received(0).ExecuteAsync(Arg.Any <Guid>(), Arg.Any <Uri>(), Arg.Any <CallbackResult>(), Arg.Any <CancellationToken>());
        }
        public async Task RemoveUncompletedWatchesAsync_TimerShouldbeResume()
        {
            // Arrange.
            var builder = new WatchArgsBuilder(this.callbackRepository);

            builder.Timeout       = TimeSpan.FromMilliseconds(500);
            builder.Confirmations = 10;

            var rule = await builder.Call(this.subject.AddTransactionAsync);

            var watches = new List <TransactionWatch <Rule> >();
            var watch   = new TransactionWatch <Rule>(rule, uint256.Zero, builder.Transaction);

            watches.Add(watch);

            await this.handler.AddWatchesAsync(watches, CancellationToken.None);

            // Act.
            await this.handler.RemoveUncompletedWatchesAsync(uint256.Zero, CancellationToken.None);

            Thread.Sleep(TimeSpan.FromMilliseconds(1000));

            // Assert.
            _ = this.callbackExecuter.Received(1).ExecuteAsync
                (
                rule.Callback.Id,
                rule.Callback.Url,
                Arg.Is <CallbackResult>
                (
                    result => result.Status == CallbackResult.StatusError
                ),
                Arg.Any <CancellationToken>()
                );

            _ = this.ruleRepository.Received(1).UpdateCurrentWatchAsync(
                Arg.Is <Guid>(id => id == rule.Id), Arg.Is <Guid?>(id => id == null), Arg.Any <CancellationToken>());
        }
        public async Task ConfirmationUpdateAsync_WithValidWatch_ShouldSuccess()
        {
            // Arrange.
            var builder = new WatchArgsBuilder(this.callbackRepository);

            builder.Timeout = TimeSpan.FromSeconds(1);
            var watch = await builder.Call(this.subject.AddTransactionAsync);

            var watches = new List <TransactionWatch <Rule> >()
            {
                new TransactionWatch <Rule>(watch, uint256.Zero, builder.Transaction),
            };

            await this.handler.AddWatchesAsync(watches, CancellationToken.None);

            // Act.
            var result = await this.handler.ConfirmationUpdateAsync(watches[0], 1, ConfirmationType.Confirmed, CancellationToken.None);

            Thread.Sleep(TimeSpan.FromSeconds(2));

            // Assert.
            Assert.False(result);

            _ = this.callbackExecuter.Received(0).ExecuteAsync
                (
                Arg.Any <Guid>(),
                Arg.Any <Uri>(),
                Arg.Any <CallbackResult>(),
                Arg.Any <CancellationToken>()
                );

            var received = await this.handler.GetCurrentWatchesAsync(CancellationToken.None);

            Assert.Single(received);
            Assert.Equal(watches[0].Id, received.First().Id);
        }
        public async Task GetCurrentWatchesAsync_WithNonEmpty_ShouldReceivedWatches()
        {
            // Arrange.
            var builder = new WatchArgsBuilder(this.callbackRepository);
            var watch1  = await builder.Call(this.subject.AddTransactionAsync);

            var watch2 = await builder.Call(this.subject.AddTransactionAsync);

            var watches = new List <TransactionWatch <Rule> >()
            {
                new TransactionWatch <Rule>(watch1, uint256.Zero, watch1.TransactionHash),
                new TransactionWatch <Rule>(watch2, uint256.Zero, watch1.TransactionHash),
            };

            await this.handler.AddWatchesAsync(watches, CancellationToken.None);

            // Act.
            var received = await this.handler.GetCurrentWatchesAsync(CancellationToken.None);

            // Assert.
            Assert.Equal(2, received.Count());
            Assert.Contains(received, w => w.Context.Id == watch1.Id);
            Assert.Contains(received, w => w.Context.Id == watch2.Id);
        }
        public async Task CreateContextsAsync_WithValidArgs_ShouldSuccess()
        {
            // Arrange.
            var tx          = Transaction.Parse(TestTransaction.Raw1, ZcoinNetworks.Instance.Mainnet);
            var untrackedTx = Transaction.Parse(TestTransaction.Raw2, ZcoinNetworks.Instance.Mainnet);

            var builder = new WatchArgsBuilder(this.callbackRepository);

            builder.Timeout     = TimeSpan.FromSeconds(1);
            builder.Transaction = tx.GetHash();
            await builder.Call(this.subject.AddTransactionAsync);

            builder.Timeout = TimeSpan.FromSeconds(2);
            await builder.Call(this.subject.AddTransactionAsync);

            // Act.
            var contexts = await this.handler.CreateContextsAsync(tx, CancellationToken.None);

            var untrackedTxContexts = await this.handler.CreateContextsAsync(untrackedTx, CancellationToken.None);

            // Assert.
            Assert.Equal(2, contexts.Count());
            Assert.Empty(untrackedTxContexts);
        }
        public async Task RemoveCompletedWatchesAsync_WithExistKey_ShouldSuccess()
        {
            // Arrange.
            var builder = new WatchArgsBuilder(this.callbackRepository);

            builder.Timeout       = TimeSpan.FromSeconds(1);
            builder.Confirmations = 10;
            var watch = await builder.Call(this.subject.AddTransactionAsync);

            var watches = new List <TransactionWatch <Rule> >()
            {
                new TransactionWatch <Rule>(watch, uint256.Zero, builder.Transaction),
            };

            await this.handler.AddWatchesAsync(watches, CancellationToken.None);

            // Act.
            await this.handler.RemoveCompletedWatchesAsync(watches, CancellationToken.None);

            // Assert.
            Assert.Empty(await this.handler.GetCurrentWatchesAsync(CancellationToken.None));
            _ = this.ruleRepository.Received(1).UpdateCurrentWatchAsync(
                Arg.Is <Guid>(id => id == watch.Id), Arg.Is <Guid?>(id => id == null), Arg.Any <CancellationToken>());
        }
        public async Task AddTransactionAsync_AndBlocksAreRemoved_TimersShouldBeResume()
        {
            // Arrange.
            await this.Initialize(CancellationToken.None);

            var(block, _) = GenerateBlock();
            block.AddTransaction(Transaction.Create(ZcoinNetworks.Instance.Regtest));

            var builder = new WatchArgsBuilder(this.callbackRepository);

            builder.Timeout     = TimeSpan.FromMilliseconds(500);
            builder.Transaction = block.Transactions[0].GetHash();

            var rule = await builder.Call(this.subject.AddTransactionAsync);

            // Act.
            await this.blockListener.BlockAddedAsync(block, 1, CancellationToken.None);

            await this.blockListener.BlockRemovingAsync(block, 1, CancellationToken.None);

            Thread.Sleep(TimeSpan.FromMilliseconds(1000));

            // Assert.
            _ = this.callbackExecuter
                .Received(1)
                .ExecuteAsync
                (
                rule.Callback.Id,
                rule.Callback.Url,
                Arg.Is <CallbackResult>
                (
                    r => r.Status == CallbackResult.StatusError
                ),
                Arg.Any <CancellationToken>()
                );
        }
        public async Task Initialize_WithNonEmptyRepository_ShouldInitializeWatches()
        {
            // Arrange.
            var tx = Transaction.Parse(TestTransaction.Raw1, ZcoinNetworks.Instance.Mainnet);

            var builder = new WatchArgsBuilder(this.callbackRepository);

            builder.Timeout     = TimeSpan.FromSeconds(1);
            builder.Transaction = tx.GetHash();

            var callback = await this.callbackRepository.AddAsync
                           (
                builder.Ip, builder.CallbackUrl, CancellationToken.None
                           );

            _ = await this.ruleRepository.AddAsync
                (
                builder.Transaction, builder.Confirmations, builder.Timeout, builder.SuccessData,
                builder.TimeoutData, callback, CancellationToken.None
                );

            // Completed watch
            var completedCallback = new Callback
                                    (
                Guid.NewGuid(),
                IPAddress.Loopback,
                DateTime.UtcNow,
                true,
                this.defaultUrl
                                    );

            var watch = await this.ruleRepository.AddAsync
                        (
                builder.Transaction, builder.Confirmations, builder.Timeout, builder.SuccessData,
                builder.TimeoutData, completedCallback, CancellationToken.None
                        );

            await this.ruleRepository.UpdateStatusAsync(watch.Id, RuleStatus.Success, CancellationToken.None);

            // Act.
            ITransactionConfirmationWatcherHandler <Rule> localHandler;
            TransactionConfirmationWatcher localWatcher;

            localHandler = localWatcher = new TransactionConfirmationWatcher
                                          (
                this.callbackRepository,
                this.ruleRepository,
                this.blockStorage,
                this.callbackExecuter,
                this.watchRepository,
                this.logger
                                          );

            await localWatcher.StartAsync(CancellationToken.None);

            var retrievedCount = (await localHandler.CreateContextsAsync(tx, CancellationToken.None)).Count();

            Thread.Sleep(TimeSpan.FromSeconds(2));

            // Assert.
            Assert.Equal(1, retrievedCount);

            _ = this.callbackExecuter.Received(1).ExecuteAsync
                (
                Arg.Any <Guid>(),
                Arg.Any <Uri>(),
                Arg.Is <CallbackResult>
                (
                    result => result.Status == CallbackResult.StatusError
                ),
                Arg.Any <CancellationToken>()
                );
        }
        public async Task AddTransactionAsync_WithNullArgs_ShouldThrow()
        {
            // Arrange.
            await this.Initialize(CancellationToken.None);

            var builder = new WatchArgsBuilder(this.callbackRepository);

            var callback = await this.callbackRepository.AddAsync(
                builder.Ip,
                builder.CallbackUrl,
                CancellationToken.None);

            // Assert.
            await Assert.ThrowsAsync <ArgumentNullException>(
                "transaction",
                () => this.subject.AddTransactionAsync(
                    null, builder.Confirmations, builder.Timeout,
                    callback, builder.SuccessData, builder.TimeoutData, CancellationToken.None)
                );

            await Assert.ThrowsAsync <ArgumentOutOfRangeException>(
                "confirmation",
                () => this.subject.AddTransactionAsync(
                    builder.Transaction, 0, builder.Timeout,
                    callback, builder.SuccessData, builder.TimeoutData, CancellationToken.None)
                );

            await Assert.ThrowsAsync <ArgumentOutOfRangeException>(
                "unconfirmedWaitingTime",
                () => this.subject.AddTransactionAsync(
                    builder.Transaction, builder.Confirmations, Ztm.Threading.TimerSchedulers.ThreadPoolScheduler.MaxDuration + TimeSpan.FromSeconds(1),
                    callback, builder.SuccessData, builder.TimeoutData, CancellationToken.None)
                );

            await Assert.ThrowsAsync <ArgumentOutOfRangeException>(
                "unconfirmedWaitingTime",
                () => this.subject.AddTransactionAsync(
                    builder.Transaction, builder.Confirmations, Ztm.Threading.TimerSchedulers.ThreadPoolScheduler.MinDuration - TimeSpan.FromSeconds(1),
                    callback, builder.SuccessData, builder.TimeoutData, CancellationToken.None)
                );

            await Assert.ThrowsAsync <ArgumentNullException>(
                "callback",
                () => this.subject.AddTransactionAsync(
                    builder.Transaction, builder.Confirmations, builder.Timeout,
                    null, builder.SuccessData, builder.TimeoutData, CancellationToken.None)
                );

            await Assert.ThrowsAsync <ArgumentNullException>(
                "successResponse",
                () => this.subject.AddTransactionAsync(
                    builder.Transaction, builder.Confirmations, builder.Timeout,
                    callback, null, builder.TimeoutData, CancellationToken.None)
                );

            await Assert.ThrowsAsync <ArgumentNullException>(
                "timeoutResponse",
                () => this.subject.AddTransactionAsync(
                    builder.Transaction, builder.Confirmations, builder.Timeout,
                    callback, builder.SuccessData, null, CancellationToken.None)
                );
        }