Files
monorepo/LCU.API.Internal.DocumentIngestor/LCU.API.Internal.DocumentIngestorTests/Services/IngestionOperationServices/MemberDocumentNotifierServiceTest.cs
T
2026-03-25 14:50:13 -05:00

606 lines
30 KiB
C#

using LCU.API.Internal.DocumentIngestor.Configurations;
using LCU.API.Internal.DocumentIngestor.Services;
using LCU.API.Internal.DocumentIngestor.Services.IngestionOperationServices;
using LCU.API.Internal.DocumentIngestor.Services.MemberNotificationInfoDeterminingServices;
using LCU.API.Internal.DocumentIngestor.Services.MicroApiServices;
using LCU.Library.Internal.NotificationsModels.Models;
using LCU.Library.DocumentType.Models.Requests;
using LCU.Library.DocumentType.Models.Responses.External;
using LCU.Library.Internal.DocumentIngestorModels.Entities;
using LCU.Library.Internal.DocumentIngestorModels.Enums;
using LCU.Library.Internal.DocumentIngestorModels.Models;
using LCU.Library.Internal.LcuMassTransitModels;
using Moq;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using LCU.API.Internal.DocumentIngestor.Services.QueueServices;
namespace LCU.API.Internal.DocumentIngestorTests.Services.IngestionOperationServices;
public class MemberDocumentNotifierServiceTest
{
private MemberDocumentNotifierService _memberDocumentNotifierService;
private Mock<IMemberNotificationInfoService> _mockMemberNotificationInfoService;
private Mock<IDocumentTypeRetrieverService> _mockDocumentTypeRetrieverService;
private Mock<IDocumentProcessingInfoService> _mockDocumentProcessingInfoService;
private Mock<INotificationApiService> _mockNotificationApiService;
private Mock<IMemberNotificationInfoDeterminingService> _mockMemberNotificationInfoDeterminingService;
private Mock<IQueueMemberNotificationCompletionService> _mockQueueMemberNotificationCompletionService;
private ApiConfiguration _apiConfiguration;
private ConnectionConfiguration _connectionConfiguration;
private ReceiveEndpointConfigurations _receiveEndpointConfigurations;
private DigitalBankingConfiguration _digitalBankingConfiguration;
[SetUp]
public void Setup()
{
_apiConfiguration = new ApiConfiguration
{
MaxMemberNotificationAttempts = 3,
MaliciousFileSearchPaddingHours = 1,
TimeZone = "America/Chicago",
NotificationEmailTemplate = @"../../../../LCU.API.Internal.DocumentIngestor/Templates/NotificationEmailTemplate.html",
NotificationEmailRowTemplate = @"../../../../LCU.API.Internal.DocumentIngestor/Templates/NotificationEmailRowTemplate.html",
FeatureFlags = new FeatureFlags() { RestrictNotificationRecipients = false },
BatchSkipAmount = 10000,
Notifications = new Notifications
{
AllowedRecipientExpression = "*"
}
};
_connectionConfiguration = new ConnectionConfiguration()
{
HostUrl = "Test",
UserName = "Test",
Password = "Test"
};
_receiveEndpointConfigurations = new ReceiveEndpointConfigurations()
{
MemberNotificationCompletionEndpoint = new ReceiveEndpointConfiguration()
{
ReceiveEndpoint = "Test",
ReceiveEndpointType = "Exchange",
PrefetchLimit = 1
}
};
_digitalBankingConfiguration = new DigitalBankingConfiguration()
{
DigitalBankingUrl = "https://digitalbanking.landmarkcu.com/eDocs",
MobileBankingApiKey = "TEST",
MobileBankingBaseUrl = "https://qa-redirect-api.qa.applications.landmarkcu.com/index.html",
MobileBankingLinkId = "a4e648ff-247a-42a8-b153-4b65d4933d1c"
};
_mockMemberNotificationInfoService = new Mock<IMemberNotificationInfoService>();
_mockDocumentTypeRetrieverService = new Mock<IDocumentTypeRetrieverService>();
_mockDocumentProcessingInfoService = new Mock<IDocumentProcessingInfoService>();
_mockNotificationApiService = new Mock<INotificationApiService>();
_mockMemberNotificationInfoDeterminingService = new Mock<IMemberNotificationInfoDeterminingService>();
_mockQueueMemberNotificationCompletionService = new Mock<IQueueMemberNotificationCompletionService>();
_memberDocumentNotifierService = new MemberDocumentNotifierService(_mockMemberNotificationInfoService.Object,
_mockDocumentTypeRetrieverService.Object,
_mockDocumentProcessingInfoService.Object,
_mockNotificationApiService.Object,
_mockMemberNotificationInfoDeterminingService.Object,
_mockQueueMemberNotificationCompletionService.Object,
_apiConfiguration,
_connectionConfiguration,
_receiveEndpointConfigurations,
_digitalBankingConfiguration);
}
[Test]
public async Task PrepareAndSendNotifications_Success()
{
var request = new MemberDocumentNotificationRequest() { RunCriticalFailureNotifications = false };
var memberNotificationInfos = GenerateMemberNotificationInfos();
var documentProcessingInfos = GenerateDocumentProcessingInfos();
var documentCodeDetailInfos = GenerateDocumentCodeDetailInfos();
var deliveryMethod = (memberNotificationInfos[2].EmailAddress, DeliveryMethod.Electronic);
List<DocumentMetadataResponse> maliciousDocuments = null;
_mockMemberNotificationInfoService
.Setup(x => x.GetSendableMemberNotificationInfosAsync(It.IsAny<DateTime>(), _apiConfiguration.MaxMemberNotificationAttempts))
.ReturnsAsync(memberNotificationInfos);
_mockDocumentProcessingInfoService.Setup(x => x.GetListByIdAsync(It.IsAny<List<int>>())).ReturnsAsync(documentProcessingInfos);
_mockMemberNotificationInfoDeterminingService.Setup(x => x.DetermineDeliveryMethod(It.IsAny<MemberNotificationInfo>())).ReturnsAsync(deliveryMethod);
_mockDocumentTypeRetrieverService.Setup(x => x.GetListOfMaliciousDocuments(It.IsAny<MaliciousDocumentSearchFilter>())).ReturnsAsync(maliciousDocuments);
_mockDocumentTypeRetrieverService.SetupSequence(x => x.GetDocumentCodeDetailByBusinessName(It.IsAny<string>()))
.ReturnsAsync(documentCodeDetailInfos[0])
.ReturnsAsync(documentCodeDetailInfos[1])
.ReturnsAsync(documentCodeDetailInfos[2]);
var result = await _memberDocumentNotifierService.PrepareAndSendNotifications(request);
Assert.NotNull(result);
Assert.IsTrue(result.IsSuccessful);
Assert.IsEmpty(result.Messages);
Assert.Zero(result.FailureCount);
_mockMemberNotificationInfoService.Verify(x => x.GetSendableMemberNotificationInfosAsync(It.IsAny<DateTime>(), It.IsAny<int>()), Times.Once);
_mockDocumentProcessingInfoService.Verify(x => x.GetListByIdAsync(It.IsAny<List<int>>()), Times.Once);
_mockMemberNotificationInfoDeterminingService.Verify(x => x.DetermineDeliveryMethod(It.IsAny<MemberNotificationInfo>()), Times.Once);
_mockDocumentTypeRetrieverService.Verify(x => x.GetListOfMaliciousDocuments(It.IsAny<MaliciousDocumentSearchFilter>()), Times.Once);
_mockDocumentTypeRetrieverService.Verify(x => x.GetDocumentCodeDetailByBusinessName(It.IsAny<string>()), Times.Exactly(3));
_mockNotificationApiService.Verify(x => x.SendEmailAsync(It.IsAny<EmailRequest>()), Times.Exactly(2));
}
[Test]
public async Task PrepareAndSendNotifications_Success_NoMemberNotificationsToSend()
{
var request = new MemberDocumentNotificationRequest() { RunCriticalFailureNotifications = false };
var memberNotificationInfos = new List<MemberNotificationInfo>();
_mockMemberNotificationInfoService
.Setup(x => x.GetSendableMemberNotificationInfosAsync(It.IsAny<DateTime>(), _apiConfiguration.MaxMemberNotificationAttempts))
.ReturnsAsync(memberNotificationInfos);
var result = await _memberDocumentNotifierService.PrepareAndSendNotifications(request);
Assert.NotNull(result);
Assert.IsTrue(result.IsSuccessful);
Assert.AreEqual(result.Messages.Count, 1);
Assert.Zero(result.FailureCount);
_mockMemberNotificationInfoService.Verify(x => x.GetSendableMemberNotificationInfosAsync(It.IsAny<DateTime>(), It.IsAny<int>()), Times.Once);
_mockDocumentProcessingInfoService.Verify(x => x.GetListByIdAsync(It.IsAny<List<int>>()), Times.Never);
_mockMemberNotificationInfoDeterminingService.Verify(x => x.DetermineDeliveryMethod(It.IsAny<MemberNotificationInfo>()), Times.Never);
_mockDocumentTypeRetrieverService.Verify(x => x.GetListOfMaliciousDocuments(It.IsAny<MaliciousDocumentSearchFilter>()), Times.Never);
_mockDocumentTypeRetrieverService.Verify(x => x.GetDocumentCodeDetailByBusinessName(It.IsAny<string>()), Times.Never);
_mockNotificationApiService.Verify(x => x.SendEmailAsync(It.IsAny<EmailRequest>()), Times.Never);
}
[Test]
public async Task PrepareAndSendNotifications_FailureCount()
{
var request = new MemberDocumentNotificationRequest() { RunCriticalFailureNotifications = false };
var memberNotificationInfos = GenerateMemberNotificationInfos();
var documentProcessingInfos = GenerateDocumentProcessingInfos();
_mockMemberNotificationInfoService
.Setup(x => x.GetSendableMemberNotificationInfosAsync(It.IsAny<DateTime>(), _apiConfiguration.MaxMemberNotificationAttempts))
.ReturnsAsync(memberNotificationInfos);
_mockDocumentProcessingInfoService.Setup(x => x.GetListByIdAsync(It.IsAny<List<int>>())).ReturnsAsync(documentProcessingInfos);
_mockDocumentProcessingInfoService.Setup(x => x.GetListByIdAsync(It.IsAny<List<int>>())).ThrowsAsync(new Exception("Failure"));
var result = await _memberDocumentNotifierService.PrepareAndSendNotifications(request);
Assert.NotNull(result);
Assert.IsTrue(result.IsFailure);
Assert.AreEqual(result.Messages.Count, 1);
Assert.AreEqual(result.FailureCount, 1);
_mockMemberNotificationInfoService.Verify(x => x.GetSendableMemberNotificationInfosAsync(It.IsAny<DateTime>(), It.IsAny<int>()), Times.Once);
_mockDocumentProcessingInfoService.Verify(x => x.GetListByIdAsync(It.IsAny<List<int>>()), Times.Once);
_mockMemberNotificationInfoDeterminingService.Verify(x => x.DetermineDeliveryMethod(It.IsAny<MemberNotificationInfo>()), Times.Never);
_mockDocumentTypeRetrieverService.Verify(x => x.GetListOfMaliciousDocuments(It.IsAny<MaliciousDocumentSearchFilter>()), Times.Never);
_mockDocumentTypeRetrieverService.Verify(x => x.GetDocumentCodeDetailByBusinessName(It.IsAny<string>()), Times.Never);
_mockNotificationApiService.Verify(x => x.SendEmailAsync(It.IsAny<EmailRequest>()), Times.Never);
}
[Test]
public async Task PrepareAndSendNotifications_Success_MaliciousFile()
{
var request = new MemberDocumentNotificationRequest() { RunCriticalFailureNotifications = false };
var memberNotificationInfos = GenerateMemberNotificationInfos();
var documentProcessingInfos = GenerateDocumentProcessingInfos();
var documentCodeDetailInfos = GenerateDocumentCodeDetailInfos();
var deliveryMethod = (memberNotificationInfos[2].EmailAddress, DeliveryMethod.Electronic);
var maliciousId = Guid.NewGuid();
var maliciousDocuments = new List<DocumentMetadataResponse>()
{
new()
{
DocumentId = maliciousId
}
};
documentProcessingInfos[1].DocumentId = maliciousId;
_mockMemberNotificationInfoService
.Setup(x => x.GetSendableMemberNotificationInfosAsync(It.IsAny<DateTime>(), _apiConfiguration.MaxMemberNotificationAttempts))
.ReturnsAsync(memberNotificationInfos);
_mockDocumentProcessingInfoService.Setup(x => x.GetListByIdAsync(It.IsAny<List<int>>())).ReturnsAsync(documentProcessingInfos);
_mockMemberNotificationInfoDeterminingService.Setup(x => x.DetermineDeliveryMethod(It.IsAny<MemberNotificationInfo>())).ReturnsAsync(deliveryMethod);
_mockDocumentTypeRetrieverService.Setup(x => x.GetListOfMaliciousDocuments(It.IsAny<MaliciousDocumentSearchFilter>())).ReturnsAsync(maliciousDocuments);
_mockDocumentTypeRetrieverService.SetupSequence(x => x.GetDocumentCodeDetailByBusinessName(It.IsAny<string>()))
.ReturnsAsync(documentCodeDetailInfos[0])
.ReturnsAsync(documentCodeDetailInfos[2]);
var result = await _memberDocumentNotifierService.PrepareAndSendNotifications(request);
Assert.NotNull(result);
Assert.IsTrue(result.IsSuccessful);
Assert.IsEmpty(result.Messages);
Assert.Zero(result.FailureCount);
_mockMemberNotificationInfoService.Verify(x => x.GetSendableMemberNotificationInfosAsync(It.IsAny<DateTime>(), It.IsAny<int>()), Times.Once);
_mockDocumentProcessingInfoService.Verify(x => x.GetListByIdAsync(It.IsAny<List<int>>()), Times.Once);
_mockDocumentTypeRetrieverService.Verify(x => x.GetListOfMaliciousDocuments(It.IsAny<MaliciousDocumentSearchFilter>()), Times.Once);
_mockDocumentTypeRetrieverService.Verify(x => x.GetDocumentCodeDetailByBusinessName(It.IsAny<string>()), Times.Exactly(2));
_mockNotificationApiService.Verify(x => x.SendEmailAsync(It.IsAny<EmailRequest>()), Times.Exactly(2));
}
[Test]
public async Task PrepareAndSendNotifications_RestrictNotificationRecipientsOff_SendsToAnyEmail()
{
var disallowedRecipient = MemberNotificationFactory();
_apiConfiguration.FeatureFlags.RestrictNotificationRecipients = false;
_apiConfiguration.Notifications.AllowedRecipientExpression = $"(?!{disallowedRecipient.EmailAddress})\\A";
var request = new MemberDocumentNotificationRequest
{
RunCriticalFailureNotifications = false
};
var memberNotificationInfos = Enumerable.Repeat(() => MemberNotificationFactory(), 3)
.Select(f => f())
.Concat(new[] { disallowedRecipient })
.ToArray();
var documentProcessingInfos = Enumerable.Repeat<Func<int, DocumentProcessingInfo>>
(
i => DocumentProcessingInfoFactory(documentProcessingInfoId: memberNotificationInfos[i].DocumentProcessingInfoId),
memberNotificationInfos.Length
)
.Select((f, i) => f(i))
.ToArray();
var documentCodeDetails = Enumerable.Repeat<Func<int, DocumentCodeDetailInfo>>
(
i => DocumentCodeDetailInfoFactory(documentBusinessName: documentProcessingInfos[i].DocumentBusinessName),
documentProcessingInfos.Length
)
.Select((f, i) => f(i))
.ToDictionary(dcd => dcd.DocumentBusinessName);
var sentEmails = new List<EmailRequest>();
_mockMemberNotificationInfoService
.Setup(mnis => mnis.GetSendableMemberNotificationInfosAsync(It.IsAny<DateTime>(), _apiConfiguration.MaxMemberNotificationAttempts))
.ReturnsAsync(memberNotificationInfos);
_mockDocumentProcessingInfoService
.Setup(dpis => dpis.GetListByIdAsync(It.IsAny<List<int>>()))
.ReturnsAsync(documentProcessingInfos);
_mockDocumentTypeRetrieverService
.Setup(dtrs => dtrs.GetListOfMaliciousDocuments(It.IsAny<MaliciousDocumentSearchFilter>()))
.ReturnsAsync((IEnumerable<DocumentMetadataResponse>)null);
_mockDocumentTypeRetrieverService
.Setup(dtrs => dtrs.GetDocumentCodeDetailByBusinessName(It.IsAny<string>()))
.Returns<string>(async businessName => documentCodeDetails.TryGetValue(businessName, out var documentCodeDetail) ? documentCodeDetail : null);
_mockNotificationApiService
.Setup(nas => nas.SendEmailAsync(It.IsAny<EmailRequest>()))
.Callback<EmailRequest>(sentEmails.Add);
var result = await _memberDocumentNotifierService.PrepareAndSendNotifications(request);
Assert.IsFalse(result.IsFailure);
Assert.IsTrue(result.IsSuccessful);
Assert.AreEqual(0, result.FailureCount);
Assert.AreEqual(memberNotificationInfos.Length, sentEmails.Select(se => se.Recipient).Distinct().Count());
}
[Test]
public async Task PrepareAndSendNotifications_RestrictNotificationRecipientsOnAndAnyUnmatched_DoesNotSendToUnmatchedEmails()
{
var disallowedRecipient = MemberNotificationFactory();
_apiConfiguration.FeatureFlags.RestrictNotificationRecipients = true;
_apiConfiguration.Notifications.AllowedRecipientExpression = $"(?!{disallowedRecipient.EmailAddress})\\A";
var request = new MemberDocumentNotificationRequest
{
RunCriticalFailureNotifications = false
};
var memberNotificationInfos = Enumerable.Repeat(() => MemberNotificationFactory(), 3)
.Select(f => f())
.Concat(new[] { disallowedRecipient })
.ToArray();
var documentProcessingInfos = Enumerable.Repeat<Func<int, DocumentProcessingInfo>>
(
i => DocumentProcessingInfoFactory(documentProcessingInfoId: memberNotificationInfos[i].DocumentProcessingInfoId),
memberNotificationInfos.Length
)
.Select((f, i) => f(i))
.ToArray();
var documentCodeDetails = Enumerable.Repeat<Func<int, DocumentCodeDetailInfo>>
(
i => DocumentCodeDetailInfoFactory(documentBusinessName: documentProcessingInfos[i].DocumentBusinessName),
documentProcessingInfos.Length
)
.Select((f, i) => f(i))
.ToDictionary(dcd => dcd.DocumentBusinessName);
var sentEmails = new List<EmailRequest>();
_mockMemberNotificationInfoService
.Setup(mnis => mnis.GetSendableMemberNotificationInfosAsync(It.IsAny<DateTime>(), _apiConfiguration.MaxMemberNotificationAttempts))
.ReturnsAsync(memberNotificationInfos);
_mockDocumentProcessingInfoService
.Setup(dpis => dpis.GetListByIdAsync(It.IsAny<List<int>>()))
.ReturnsAsync(documentProcessingInfos);
_mockDocumentTypeRetrieverService
.Setup(dtrs => dtrs.GetListOfMaliciousDocuments(It.IsAny<MaliciousDocumentSearchFilter>()))
.ReturnsAsync((IEnumerable<DocumentMetadataResponse>)null);
_mockDocumentTypeRetrieverService
.Setup(dtrs => dtrs.GetDocumentCodeDetailByBusinessName(It.IsAny<string>()))
.Returns<string>(async businessName => documentCodeDetails.TryGetValue(businessName, out var documentCodeDetail) ? documentCodeDetail : null);
_mockNotificationApiService
.Setup(nas => nas.SendEmailAsync(It.IsAny<EmailRequest>()))
.Callback<EmailRequest>(sentEmails.Add);
var result = await _memberDocumentNotifierService.PrepareAndSendNotifications(request);
Assert.IsTrue(result.IsFailure);
Assert.IsFalse(result.IsSuccessful);
Assert.AreEqual(1, result.FailureCount);
Assert.AreEqual(0, sentEmails.Count(se => se.Recipient.Contains(disallowedRecipient.EmailAddress)));
}
[Test]
public async Task PrepareAndSendNotifications_RestrictNotificationRecipientsOnAndAllMatched_SendsToMatchedEmails()
{
var disallowedRecipient = MemberNotificationFactory();
_apiConfiguration.FeatureFlags.RestrictNotificationRecipients = true;
_apiConfiguration.Notifications.AllowedRecipientExpression = $"(?!{disallowedRecipient.EmailAddress})\\A";
var request = new MemberDocumentNotificationRequest
{
RunCriticalFailureNotifications = false
};
var allowedMemberNotificationInfos = Enumerable.Repeat(() => MemberNotificationFactory(), 3)
.Select(f => f())
.ToArray();
var memberNotificationInfos = allowedMemberNotificationInfos
.Concat(new[] { disallowedRecipient })
.ToArray();
var documentProcessingInfos = Enumerable.Repeat<Func<int, DocumentProcessingInfo>>
(
i => DocumentProcessingInfoFactory(documentProcessingInfoId: memberNotificationInfos[i].DocumentProcessingInfoId),
memberNotificationInfos.Length
)
.Select((f, i) => f(i))
.ToArray();
var documentCodeDetails = Enumerable.Repeat<Func<int, DocumentCodeDetailInfo>>
(
i => DocumentCodeDetailInfoFactory(documentBusinessName: documentProcessingInfos[i].DocumentBusinessName),
documentProcessingInfos.Length
)
.Select((f, i) => f(i))
.ToDictionary(dcd => dcd.DocumentBusinessName);
var sentEmails = new List<EmailRequest>();
_mockMemberNotificationInfoService
.Setup(mnis => mnis.GetSendableMemberNotificationInfosAsync(It.IsAny<DateTime>(), _apiConfiguration.MaxMemberNotificationAttempts))
.ReturnsAsync(memberNotificationInfos);
_mockDocumentProcessingInfoService
.Setup(dpis => dpis.GetListByIdAsync(It.IsAny<List<int>>()))
.ReturnsAsync(documentProcessingInfos);
_mockDocumentTypeRetrieverService
.Setup(dtrs => dtrs.GetListOfMaliciousDocuments(It.IsAny<MaliciousDocumentSearchFilter>()))
.ReturnsAsync((IEnumerable<DocumentMetadataResponse>)null);
_mockDocumentTypeRetrieverService
.Setup(dtrs => dtrs.GetDocumentCodeDetailByBusinessName(It.IsAny<string>()))
.Returns<string>(async businessName => documentCodeDetails.TryGetValue(businessName, out var documentCodeDetail) ? documentCodeDetail : null);
_mockNotificationApiService
.Setup(nas => nas.SendEmailAsync(It.IsAny<EmailRequest>()))
.Callback<EmailRequest>(sentEmails.Add);
await _memberDocumentNotifierService.PrepareAndSendNotifications(request);
var allowedEmails = allowedMemberNotificationInfos.Select(mni => mni.EmailAddress);
Assert.AreEqual(allowedMemberNotificationInfos.Length, sentEmails.Count(se => se.Recipient.Any(r => allowedEmails.Contains(r))));
}
[Test]
public async Task PrepareAndSendNotifications_Failure_NullEmails()
{
var request = new MemberDocumentNotificationRequest() { RunCriticalFailureNotifications = false };
var memberNotificationInfos = GenerateMemberNotificationInfos();
memberNotificationInfos[0].EmailAddress = "";
memberNotificationInfos[1].EmailAddress = "";
memberNotificationInfos[2].EmailAddress = "";
var documentProcessingInfos = GenerateDocumentProcessingInfos();
var documentCodeDetailInfos = GenerateDocumentCodeDetailInfos();
var deliveryMethod = (memberNotificationInfos[2].EmailAddress, DeliveryMethod.Electronic);
var maliciousId = Guid.NewGuid();
List<DocumentMetadataResponse> maliciousDocuments = null;
_mockMemberNotificationInfoService
.Setup(x => x.GetSendableMemberNotificationInfosAsync(It.IsAny<DateTime>(), _apiConfiguration.MaxMemberNotificationAttempts))
.ReturnsAsync(memberNotificationInfos);
_mockDocumentProcessingInfoService.Setup(x => x.GetListByIdAsync(It.IsAny<List<int>>())).ReturnsAsync(documentProcessingInfos);
_mockMemberNotificationInfoDeterminingService.Setup(x => x.DetermineDeliveryMethod(It.IsAny<MemberNotificationInfo>())).ReturnsAsync(deliveryMethod);
_mockDocumentTypeRetrieverService.Setup(x => x.GetListOfMaliciousDocuments(It.IsAny<MaliciousDocumentSearchFilter>())).ReturnsAsync(maliciousDocuments);
var result = await _memberDocumentNotifierService.PrepareAndSendNotifications(request);
Assert.NotNull(result);
Assert.IsTrue(result.IsSuccessful);
Assert.AreEqual(result.Messages.Count, 0);
Assert.AreEqual(result.FailureCount, 0);
_mockMemberNotificationInfoService.Verify(x => x.GetSendableMemberNotificationInfosAsync(It.IsAny<DateTime>(), It.IsAny<int>()), Times.Once);
_mockDocumentProcessingInfoService.Verify(x => x.GetListByIdAsync(It.IsAny<List<int>>()), Times.Once);
_mockDocumentTypeRetrieverService.Verify(x => x.GetListOfMaliciousDocuments(It.IsAny<MaliciousDocumentSearchFilter>()), Times.Once);
_mockDocumentTypeRetrieverService.Verify(x => x.GetDocumentCodeDetailByBusinessName(It.IsAny<string>()), Times.Never);
_mockQueueMemberNotificationCompletionService.Verify(x => x.QueueMemberNotificationCompletionRequest(It.IsAny<MemberNotificationCompletionRequest>(),
It.IsAny<bool>(),
It.IsAny<List<string>>()), Times.Exactly(2));
_mockNotificationApiService.Verify(x => x.SendEmailAsync(It.IsAny<EmailRequest>()), Times.Never);
}
[Test]
public void PrepareAndSendNotifications_ArgumentNullException()
{
MemberDocumentNotificationRequest request = null;
Assert.ThrowsAsync<ArgumentNullException>(async () => await _memberDocumentNotifierService.HandleMemberDocumentNotification(request));
_mockMemberNotificationInfoService.Verify(x => x.GetSendableMemberNotificationInfosAsync(It.IsAny<DateTime>(), It.IsAny<int>()), Times.Never);
_mockMemberNotificationInfoDeterminingService.Verify(x => x.DetermineDeliveryMethod(It.IsAny<MemberNotificationInfo>()), Times.Never);
_mockDocumentProcessingInfoService.Verify(x => x.GetListByIdAsync(It.IsAny<List<int>>()), Times.Never);
_mockNotificationApiService.Verify(x => x.SendEmailAsync(It.IsAny<EmailRequest>()), Times.Never);
}
[Test]
public async Task PrepareAndSendNotifications_BreakLargeChunk_Success()
{
var request = new MemberDocumentNotificationRequest() { RunCriticalFailureNotifications = false };
var repeatAmount = 15000;
var randomInt = new Random();
var memberNotificationInfos = Enumerable.Repeat(1, repeatAmount)
.Select(_ => GenerateSingleMemberNotificationInfo(randomInt.Next(1, repeatAmount))).ToList();
var documentProcessingInfos = Enumerable.Repeat(1, repeatAmount)
.Select(_ => GenerateSingleDocumentProcessingInfo(Guid.NewGuid())).ToList();
var documentCodeDetailInfo = GenerateDocumentCodeDetailInfos().FirstOrDefault();
List<DocumentMetadataResponse> maliciousDocuments = null;
_mockMemberNotificationInfoService
.Setup(x => x.GetSendableMemberNotificationInfosAsync(It.IsAny<DateTime>(), _apiConfiguration.MaxMemberNotificationAttempts))
.ReturnsAsync(memberNotificationInfos);
_mockDocumentProcessingInfoService.SetupSequence(x => x.GetListByIdAsync(It.IsAny<List<int>>()))
.ReturnsAsync(documentProcessingInfos.Skip(0).Take(_apiConfiguration.BatchSkipAmount).ToList())
.ReturnsAsync(documentProcessingInfos.Skip(_apiConfiguration.BatchSkipAmount).Take(_apiConfiguration.BatchSkipAmount).ToList());
_mockDocumentTypeRetrieverService.Setup(x => x.GetListOfMaliciousDocuments(It.IsAny<MaliciousDocumentSearchFilter>())).ReturnsAsync(maliciousDocuments);
_mockDocumentTypeRetrieverService.Setup(x => x.GetDocumentCodeDetailByBusinessName(It.IsAny<string>()))
.ReturnsAsync(() => documentCodeDetailInfo);
var result = await _memberDocumentNotifierService.PrepareAndSendNotifications(request);
Assert.NotNull(result);
Assert.IsTrue(result.IsSuccessful);
Assert.IsEmpty(result.Messages);
Assert.Zero(result.FailureCount);
_mockMemberNotificationInfoService.Verify(x => x.GetSendableMemberNotificationInfosAsync(It.IsAny<DateTime>(), It.IsAny<int>()), Times.Once);
_mockMemberNotificationInfoService.Verify(x => x.UpdateRange(It.IsAny<IEnumerable<MemberNotificationInfo>>()), Times.Exactly(2));
_mockDocumentProcessingInfoService.Verify(x => x.GetListByIdAsync(It.IsAny<List<int>>()), Times.Exactly(2));
_mockMemberNotificationInfoDeterminingService.Verify(x => x.DetermineDeliveryMethod(It.IsAny<MemberNotificationInfo>()), Times.Never);
_mockDocumentTypeRetrieverService.Verify(x => x.GetListOfMaliciousDocuments(It.IsAny<MaliciousDocumentSearchFilter>()), Times.Once);
_mockDocumentTypeRetrieverService.Verify(x => x.GetDocumentCodeDetailByBusinessName(It.IsAny<string>()), Times.Exactly(repeatAmount));
_mockNotificationApiService.Verify(x => x.SendEmailAsync(It.IsAny<EmailRequest>()), Times.Once);
}
const string letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvqxyz";
private static readonly Random Random = new(DateTime.Now.Millisecond);
private static string RandomString => new(Enumerable.Repeat(() => letters[Random.Next(letters.Length)], 10).Select(c => c()).ToArray());
private static Random IdSeries = new();
private static MemberNotificationInfo MemberNotificationFactory
(
string? emailAddress = null,
string? agreeUserId = null,
int? documentProcessingInfoId = null,
NotificationStatusTypes? notificationStatusType = null
)
=> new()
{
MemberNotificationInfoId = IdSeries.Next(),
AgreeUserId = agreeUserId ?? $"{IdSeries.Next()}{IdSeries.Next()}{IdSeries.Next()}",
EmailAddress = emailAddress ?? $"{RandomString}@{RandomString}.com",
EntityName = Guid.NewGuid().ToString(),
DeliveryMethod = DeliveryMethod.Electronic,
DocumentProcessingInfoId = documentProcessingInfoId ?? IdSeries.Next(),
Attempts = 0,
NotificationStatusType = notificationStatusType ?? NotificationStatusTypes.Pending,
SentTimeStamp = DateTime.UtcNow,
UploadDate = DateTime.UtcNow
};
private static List<MemberNotificationInfo> GenerateMemberNotificationInfos()
{
return new List<MemberNotificationInfo>
{
MemberNotificationFactory
(
agreeUserId: "123",
emailAddress: "[email protected]",
documentProcessingInfoId: 5,
notificationStatusType: NotificationStatusTypes.Pending
),
MemberNotificationFactory
(
agreeUserId: "123",
emailAddress: "[email protected]",
documentProcessingInfoId: 6,
notificationStatusType: NotificationStatusTypes.Pending
),
MemberNotificationFactory
(
agreeUserId: "789",
emailAddress: "[email protected]",
documentProcessingInfoId: 7,
notificationStatusType: NotificationStatusTypes.Failure
)
};
}
private static DocumentProcessingInfo DocumentProcessingInfoFactory
(
int? documentProcessingInfoId = null
) => new()
{
DocumentId = new Guid(),
DocumentProcessingInfoId = documentProcessingInfoId ?? IdSeries.Next(),
DocumentFileName = Guid.NewGuid().ToString(),
DocumentBusinessName = Guid.NewGuid().ToString()
};
private static List<DocumentProcessingInfo> GenerateDocumentProcessingInfos()
{
return new List<DocumentProcessingInfo>()
{
DocumentProcessingInfoFactory
(
documentProcessingInfoId: 5
),
DocumentProcessingInfoFactory
(
documentProcessingInfoId: 6
),
DocumentProcessingInfoFactory
(
documentProcessingInfoId: 7
)
};
}
private static DocumentCodeDetailInfo DocumentCodeDetailInfoFactory
(
string? documentBusinessName = null
) => new()
{
DocumentBusinessName = documentBusinessName ?? Guid.NewGuid().ToString(),
DocumentMemberFacingName = Guid.NewGuid().ToString()
};
private static List<DocumentCodeDetailInfo> GenerateDocumentCodeDetailInfos()
{
return new List<DocumentCodeDetailInfo>
{
DocumentCodeDetailInfoFactory
(
documentBusinessName: "5498-SA HSA Contributions (Tax)"
),
DocumentCodeDetailInfoFactory
(
documentBusinessName: "1099-MISC"
),
DocumentCodeDetailInfoFactory
(
documentBusinessName: "5498 (Retirement Plan Contributions) (Tax)"
)
};
}
public MemberNotificationInfo GenerateSingleMemberNotificationInfo(int id)
{
return new MemberNotificationInfo()
{
MemberNotificationInfoId = id,
AgreeUserId = "123",
EmailAddress = "[email protected]",
EntityName = "Fred",
DeliveryMethod = DeliveryMethod.Electronic,
DocumentProcessingInfoId = 5,
Attempts = 1,
NotificationStatusType = NotificationStatusTypes.Pending,
SentTimeStamp = DateTime.UtcNow,
UploadDate = DateTime.UtcNow
};
}
public DocumentProcessingInfo GenerateSingleDocumentProcessingInfo(Guid id)
{
return new DocumentProcessingInfo()
{
DocumentId = id,
DocumentProcessingInfoId = 5,
DocumentFileName = "FileName1",
DocumentBusinessName = "5498-SA HSA Contributions (Tax)"
};
}
}