Exemplo n.º 1
0
        public async Task ProcessEmailNotificationsAsyncTests(
            CircuitBreakerDetails circuitBreakerDetails,
            int batchAccountSize,
            SendNotificationResponse sendNotificationResponse,
            bool throwSendNotificationException = false,
            int halfOpenRetryMax = 5)
        {
            // Configure Calls
            A.CallTo(() => fakeAccountsService.GetCircuitBreakerStatusAsync()).Returns(circuitBreakerDetails);
            A.CallTo(() => fakeAccountsService.GetNextBatchOfEmailsAsync(A <int> ._)).Returns(GetAccountsToProcess(batchAccountSize));
            if (throwSendNotificationException)
            {
                A.CallTo(() => fakeSendCitizenNotificationService.SendCitizenNotificationAsync(A <Account> ._)).Throws(() => new Exception(nameof(Exception), new Exception(nameof(Exception))));
            }
            else
            {
                A.CallTo(() => fakeSendCitizenNotificationService.SendCitizenNotificationAsync(A <Account> ._)).Returns(sendNotificationResponse);
            }

            A.CallTo(() => fakeConfiguration.GetConfig <int>(A <string> ._))
            .Returns(halfOpenRetryMax);

            //For this test the function call is not diasbled in the config.
            A.CallTo(() => fakeConfiguration.GetConfig <bool>(A <string> ._)).Returns(false);

            // Assign
            var emailProcessor = new EmailNotificationProcessor(fakeSendCitizenNotificationService, fakeApplicationLogger, fakeConfiguration, fakeAccountsService);

            // Act
            await emailProcessor.ProcessEmailNotificationsAsync();

            // Assert
            A.CallTo(() => fakeAccountsService.GetCircuitBreakerStatusAsync()).MustHaveHappened();

            if (circuitBreakerDetails.CircuitBreakerStatus != CircuitBreakerStatus.Open)
            {
                if (throwSendNotificationException)
                {
                    A.CallTo(() =>
                             fakeAccountsService.InsertAuditAsync(A <AccountNotificationAudit> .That.Matches(audit =>
                                                                                                             audit.NotificationProcessingStatus == NotificationProcessingStatus.Failed && !string.IsNullOrWhiteSpace(audit.Note))))
                    .MustHaveHappened();
                    A.CallTo(() => fakeAccountsService.HalfOpenCircuitBreakerAsync()).MustHaveHappened();
                    if (circuitBreakerDetails.HalfOpenRetryCount == halfOpenRetryMax)
                    {
                        A.CallTo(() => fakeAccountsService.SetBatchToCircuitGotBrokenAsync(A <IEnumerable <Account> > ._)).MustHaveHappened();

                        A.CallTo(() => fakeAccountsService.OpenCircuitBreakerAsync()).MustHaveHappened();
                    }
                }
                else
                {
                    if (sendNotificationResponse.Success)
                    {
                        A.CallTo(() =>
                                 fakeAccountsService.InsertAuditAsync(A <AccountNotificationAudit> ._)).MustHaveHappened(batchAccountSize, Times.Exactly);
                        if (circuitBreakerDetails.CircuitBreakerStatus == CircuitBreakerStatus.HalfOpen)
                        {
                            A.CallTo(() =>
                                     fakeAccountsService.CloseCircuitBreakerAsync()).MustHaveHappened();
                        }
                    }
                    else
                    {
                        if (sendNotificationResponse.RateLimitException)
                        {
                            A.CallTo(() => fakeAccountsService.OpenCircuitBreakerAsync()).MustHaveHappened();
                            A.CallTo(() =>
                                     fakeAccountsService.SetBatchToCircuitGotBrokenAsync(
                                         A <IEnumerable <Account> > ._))
                            .MustHaveHappened();
                            A.CallTo(() => fakeApplicationLogger.Info(A <string> ._)).MustHaveHappened();
                        }
                        else
                        {
                            A.CallTo(() =>
                                     fakeAccountsService.InsertAuditAsync(A <AccountNotificationAudit> .That.Matches(audit =>
                                                                                                                     audit.NotificationProcessingStatus == NotificationProcessingStatus.Failed)))
                            .MustHaveHappened();
                        }
                    }
                }
            }
            else
            {
                A.CallTo(() => fakeAccountsService.GetNextBatchOfEmailsAsync(A <int> ._)).MustNotHaveHappened();
                A.CallTo(() => fakeSendCitizenNotificationService.SendCitizenNotificationAsync(A <Account> ._)).MustNotHaveHappened();
            }
        }
        private async Task SendNextBatchOfEmailsAsync()
        {
            var circuitBreaker = await accountsService.GetCircuitBreakerStatusAsync();

            if (circuitBreaker.CircuitBreakerStatus != CircuitBreakerStatus.Open)
            {
                var batchSize = configuration.GetConfig <int>(Constants.BatchSize);

                var emailBatch = await accountsService.GetNextBatchOfEmailsAsync(batchSize);

                var accountsToProcess = emailBatch.ToList();
                applicationLogger.Trace($"About to process email notifications with a batch size of {accountsToProcess.Count}");

                var halfOpenCountAllowed = configuration.GetConfig <int>(Constants.GovUkNotifyRetryCount);

                foreach (var account in accountsToProcess)
                {
                    try
                    {
                        var serviceResponse = await sendCitizenNotificationService.SendCitizenNotificationAsync(account);

                        if (serviceResponse.RateLimitException)
                        {
                            await accountsService.OpenCircuitBreakerAsync();

                            applicationLogger.Info(
                                $"RateLimit Exception thrown now resetting the unprocessed email notifications");

                            await accountsService.SetBatchToCircuitGotBrokenAsync(
                                accountsToProcess.Where(notification => !notification.Processed));

                            break;
                        }

                        await accountsService.InsertAuditAsync(new AccountNotificationAudit
                        {
                            Email = account.EMail,
                            NotificationProcessingStatus = serviceResponse.Success
                                ? NotificationProcessingStatus.Completed
                                : NotificationProcessingStatus.Failed
                        });

                        if (serviceResponse.Success &&
                            circuitBreaker.CircuitBreakerStatus == CircuitBreakerStatus.HalfOpen)
                        {
                            await accountsService.CloseCircuitBreakerAsync();
                        }

                        account.Processed = true;
                    }
                    catch (Exception exception)
                    {
                        await accountsService.InsertAuditAsync(new AccountNotificationAudit
                        {
                            Email = account.EMail,
                            NotificationProcessingStatus = NotificationProcessingStatus.Failed,
                            Note = exception.InnerException?.Message
                        });

                        await accountsService.HalfOpenCircuitBreakerAsync();

                        applicationLogger.ErrorJustLogIt("Exception whilst sending email notification", exception);
                        circuitBreaker = await accountsService.GetCircuitBreakerStatusAsync();

                        if (circuitBreaker.CircuitBreakerStatus == CircuitBreakerStatus.HalfOpen &&
                            circuitBreaker.HalfOpenRetryCount == halfOpenCountAllowed)
                        {
                            await accountsService.OpenCircuitBreakerAsync();

                            //Set the all the accountsin the batch that did not get processed (sent ok)  to CircuitGotBroken
                            await accountsService.SetBatchToCircuitGotBrokenAsync(
                                accountsToProcess.Where(notification => !notification.Processed));

                            break;
                        }
                    }
                }

                applicationLogger.Trace("Completed processing all accounts in the batch");
            }
            else
            {
                applicationLogger.Info("Circuit is open so no account processed");
            }
        }