606 lines
30 KiB
C#
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)"
|
|
|
|
};
|
|
}
|
|
} |