diff --git a/LCU.API.Internal.MemberPreferencesApi/.dockerignore b/LCU.API.Internal.MemberPreferencesApi/.dockerignore
new file mode 100644
index 0000000..186c6b6
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/.dockerignore
@@ -0,0 +1,17 @@
+**/*.bat
+**/*.log
+**/*.md
+**/*.yml
+**/.dockerignore
+**/.editorconfig
+**/.git
+**/.gitattributes
+**/.gitignore
+**/.vs
+**/.vscode
+**/bin
+**/dist
+**/dockerfile
+**/node_modules
+**/obj
+**/package-lock.json
diff --git a/LCU.API.Internal.MemberPreferencesApi/.editorconfig b/LCU.API.Internal.MemberPreferencesApi/.editorconfig
new file mode 100644
index 0000000..db4457c
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/.editorconfig
@@ -0,0 +1,8 @@
+root = true
+
+[*]
+charset = utf-8
+insert_final_newline = true
+indent_size = 4
+indent_style = space
+trim_trailing_whitespace = true
diff --git a/LCU.API.Internal.MemberPreferencesApi/.gitignore b/LCU.API.Internal.MemberPreferencesApi/.gitignore
new file mode 100644
index 0000000..3e759b7
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/.gitignore
@@ -0,0 +1,330 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+**/Properties/launchSettings.json
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
diff --git a/LCU.API.Internal.MemberPreferencesApi/Dockerfile b/LCU.API.Internal.MemberPreferencesApi/Dockerfile
new file mode 100644
index 0000000..1480b48
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/Dockerfile
@@ -0,0 +1,37 @@
+# .NET Core SDK
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS aspnetcore-build
+
+# Copy Projects
+COPY LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.csproj ./source/LCU.API.Internal.MemberPreferencesApi/
+COPY LCU.API.Internal.MemberPreferencesApiLib/LCU.API.Internal.MemberPreferencesApiLib.csproj ./source/LCU.API.Internal.MemberPreferencesApiLib/
+COPY LCU.Library.Internal.MemberPreferencesApiModels/LCU.Library.Internal.MemberPreferencesApiModels.csproj ./source/LCU.Library.Internal.MemberPreferencesApiModels/
+
+# Install Credential Provider and set env variables to enable Nuget restore with auth
+ARG PAT=bh2lq3jibktw67b2wdbqyfln7cd3je6lk52kijntpj43v6kimpgq
+RUN curl -L https://raw.githubusercontent.com/Microsoft/artifacts-credprovider/master/helpers/installcredprovider.sh | sh
+
+ENV NUGET_CREDENTIALPROVIDER_SESSIONTOKENCACHE_ENABLED true
+ENV VSS_NUGET_EXTERNAL_FEED_ENDPOINTS "{\"endpointCredentials\": [{\"endpoint\":\"https://landmarkcu.pkgs.visualstudio.com/_packaging/LCU-Internal/nuget/v3/index.json\", \"password\":\"${PAT}\"}]}"
+
+RUN dotnet restore -s "https://landmarkcu.pkgs.visualstudio.com/_packaging/LCU-Internal/nuget/v3/index.json" -s "https://api.nuget.org/v3/index.json" ./source/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.csproj
+
+# Copy All Files
+COPY . ./source/
+
+LABEL test=true
+COPY LCU.API.Internal.MemberPreferencesApi.UnitTests/LCU.API.Internal.MemberPreferencesApi.UnitTests.csproj ./source/LCU.API.Internal.MemberPreferencesApi.UnitTests/
+RUN dotnet restore ./source/LCU.API.Internal.MemberPreferencesApi.UnitTests/LCU.API.Internal.MemberPreferencesApi.UnitTests.csproj
+RUN dotnet test -c Release --results-directory /testresults --logger "trx;LogFileName=test_results.trx" ./source/LCU.API.Internal.MemberPreferencesApi.UnitTests/LCU.API.Internal.MemberPreferencesApi.UnitTests.csproj
+
+# .NET Core Publish
+RUN dotnet publish ./source/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.csproj -c Release -o /dist
+
+# ASP.NET Core
+FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine3.20
+RUN apk add --no-cache icu-libs && apk add --no-cache tzdata;
+ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
+ENV ASPNETCORE_ENVIRONMENT=Development
+
+WORKDIR /app
+COPY --from=aspnetcore-build /dist .
+ENTRYPOINT ["dotnet", "LCU.API.Internal.MemberPreferencesApi.dll"]
diff --git a/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.IntegrationTests/LCU.API.Internal.MemberPreferencesApi.IntegrationTests.csproj b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.IntegrationTests/LCU.API.Internal.MemberPreferencesApi.IntegrationTests.csproj
new file mode 100644
index 0000000..f404962
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.IntegrationTests/LCU.API.Internal.MemberPreferencesApi.IntegrationTests.csproj
@@ -0,0 +1,29 @@
+
+
+
+ Exe
+ net8.0
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.IntegrationTests/XdiTransferTest.cs b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.IntegrationTests/XdiTransferTest.cs
new file mode 100644
index 0000000..3a2567f
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.IntegrationTests/XdiTransferTest.cs
@@ -0,0 +1,181 @@
+using System;
+using System.Collections.Generic;
+using Moq;
+using NUnit.Framework;
+using LCU.API.Internal.MemberPreferencesApiLib.Repositories;
+using LCU.API.Internal.MemberPreferencesApi.Services.XdiServices;
+using LCU.API.Internal.MemberPreferencesApi.Configuration;
+using LCU.Library.Internal.MemberPreferencesApiModels.Entities;
+using LCU.Library.Internal.MemberPreferencesApiModels.Enums;
+using LCU.Library.Internal.LcuMassTransitModels;
+using LCU.API.Internal.MemberPreferencesApi.MicroApiServices;
+
+namespace LCU.API.Internal.MemberPreferencesApi.IntegrationTests
+{
+ public class XdiTransferTest
+ {
+ private IXdiBatchService _xdiBatchItemService;
+ private Mock _mockBatchItemsRepository;
+ private Mock _mockUnitOfWork;
+ private XdiSftpApiConfiguration _xdiSftpConfiguration;
+ private FtpApiConfiguration _ftpConfiguration;
+ private Mock _mockUnifiedOptinRepository;
+ private Mock _mockMemberPreferenceRepository;
+ private Mock _mockFtpApiService;
+ private ConnectionConfiguration _connectionConfiguration;
+ private ReceiveEndpointConfiguration _completionReceiveEndpoint;
+ private ReceiveEndpointConfigurations _endpointConfiguration;
+
+ [SetUp]
+ public void Setup()
+ {
+ _mockFtpApiService = new Mock();
+ _connectionConfiguration = new ConnectionConfiguration();
+ _mockBatchItemsRepository = new Mock();
+ _mockMemberPreferenceRepository = new Mock();
+ _endpointConfiguration = new ReceiveEndpointConfigurations();
+ _completionReceiveEndpoint = _endpointConfiguration.XdiBatchConfirmEndpoint;
+ _mockUnitOfWork = new Mock();
+ _ftpConfiguration = new FtpApiConfiguration()
+ {
+ Url = "https://qa-ftpapi.lcu-internal.com/api/"
+ };
+ _xdiSftpConfiguration = new XdiSftpApiConfiguration()
+
+ {
+ HostName = "sftp.xdi.com",
+ UserName = "LANDMARKCU",
+ PortNumber = 22,
+ Password = "FILLMEIN",
+ Fingerprint = "ssh-dss 2048 12:78:33:0a:cf:71:21:e2:8b:0e:ef:6a:8a:c6:8e:4f"
+ };
+
+ _mockUnifiedOptinRepository = new Mock();
+ _xdiBatchItemService = new XdiBatchService(_mockBatchItemsRepository.Object, _mockUnitOfWork.Object,
+ _xdiSftpConfiguration, _mockUnifiedOptinRepository.Object, _mockMemberPreferenceRepository.Object,
+ _connectionConfiguration, _endpointConfiguration, _mockFtpApiService.Object);
+ }
+
+
+ [Ignore("This will actually create the items and FTP them to XDI Test Folder. Be Careful. Fill in password.")]
+ public void Test_Send_Test_Files()
+ {
+ IReadOnlyCollection items = new List()
+ {
+ new XdiBatchItem()
+ {
+ Created = DateTime.Now,
+ BatchState = BatchState.New,
+ LastUpdated = DateTime.Now,
+ UserId = "99"
+ },
+ new XdiBatchItem()
+ {
+ Created = DateTime.Now,
+ BatchState = BatchState.New,
+ LastUpdated = DateTime.Now,
+ UserId = "100"
+ },
+ new XdiBatchItem()
+ {
+ Created = DateTime.Now,
+ BatchState = BatchState.New,
+ LastUpdated = DateTime.Now,
+ UserId = "101"
+ },
+ new XdiBatchItem()
+ {
+ Created = DateTime.Now,
+ BatchState = BatchState.New,
+ LastUpdated = DateTime.Now,
+ UserId = "102"
+ },
+ new XdiBatchItem()
+ {
+ Created = DateTime.Now,
+ BatchState = BatchState.New,
+ LastUpdated = DateTime.Now,
+ UserId = "103"
+ }
+ };
+
+ var settings = new List()
+ {
+ new MemberOptInSettings()
+ {
+ Id = 1000,
+ UniversalOptIn = DeliveryPreference.Paper,
+ ENotifications = DeliveryPreference.Electronic,
+ EStatements = DeliveryPreference.Electronic,
+ UnifiedOptInEmail = "test@test.com",
+ ENotificationsEmail = "test@test.com",
+ EStatementsEmail = "test@test.com",
+ LastUpdated = DateTime.Now,
+ LegalDate = DateTime.Now,
+ LegalVersion = "1.2.3",
+ UserId = "99",
+ CreatedByType = UserType.Associate,
+ LastUpdatedByType = UserType.System,
+ CreatedBy = "test",
+ Created = DateTime.Now,
+ LastUpdatedBy = "test"
+ },
+ new MemberOptInSettings()
+ {
+ Id = 1001,
+ UniversalOptIn = DeliveryPreference.Paper,
+ ENotifications = null,
+ EStatements = DeliveryPreference.Electronic,
+ UnifiedOptInEmail = "test@test.com",
+ ENotificationsEmail = null,
+ EStatementsEmail = "test@test.com",
+ UserId = "100"
+ },
+ new MemberOptInSettings()
+ {
+ Id = 1002,
+ UniversalOptIn = DeliveryPreference.Electronic,
+ ENotifications = DeliveryPreference.Paper,
+ EStatements = DeliveryPreference.Paper,
+ UnifiedOptInEmail = "test@test.com",
+ ENotificationsEmail = "test@test.com",
+ EStatementsEmail = "test@test.com",
+ UserId = "101"
+ },
+ new MemberOptInSettings()
+ {
+ Id = 1003,
+ UniversalOptIn = DeliveryPreference.Electronic,
+ ENotifications = null,
+ EStatements = DeliveryPreference.Paper,
+ UnifiedOptInEmail = "test@test.com",
+ ENotificationsEmail = null,
+ EStatementsEmail = null,
+ UserId = "102"
+ },
+ new MemberOptInSettings()
+ {
+ Id = 1004,
+ UniversalOptIn = DeliveryPreference.Electronic,
+ ENotifications = DeliveryPreference.Electronic,
+ EStatements = DeliveryPreference.Electronic,
+ UnifiedOptInEmail = "updated_email@test.com",
+ ENotificationsEmail = "test@test.com",
+ EStatementsEmail = "test@test.com",
+ UserId = "103"
+ }
+ };
+
+ _mockBatchItemsRepository.Setup(x => x.GetXdiBatchItems()).ReturnsAsync(items);
+
+ foreach (var setting in settings)
+ {
+ _mockUnifiedOptinRepository.Setup(x => x.GetMemberOptInSettings(setting.UserId))
+ .ReturnsAsync(setting);
+ }
+
+ _mockBatchItemsRepository.Setup(x => x.SaveAsync(It.IsAny())).Verifiable();
+ _mockBatchItemsRepository.Setup(x => x.Delete(It.IsAny())).Verifiable();
+ }
+ }
+}
diff --git a/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Controllers/MemberEmailControllerTest.cs b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Controllers/MemberEmailControllerTest.cs
new file mode 100644
index 0000000..5f0d30d
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Controllers/MemberEmailControllerTest.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Threading.Tasks;
+using LCU.API.Internal.MemberPreferencesApi.Services;
+using Microsoft.AspNetCore.Mvc.Infrastructure;
+using Microsoft.FeatureManagement;
+using Moq;
+using NUnit.Framework;
+using LCU.API.Internal.MemberPreferencesApi.Controllers;
+
+namespace LCU.API.Internal.MemberPreferencesApi.UnitTests.Controllers
+{
+ public class MemberEmailControllerTest
+ {
+ private readonly MemberEmailController _controller;
+
+ private readonly Mock _service = new Mock();
+ private readonly Mock _featureManager = new Mock();
+
+ public MemberEmailControllerTest()
+ {
+ _controller = new MemberEmailController(_service.Object, _featureManager.Object);
+ }
+
+ [Test]
+ public async Task EmailSentSuccessfully_AllCases_CallsServiceUnifiedOptInEmailFailedToSend()
+ {
+ var userId = Guid.NewGuid().ToString();
+ var emailAddress = Guid.NewGuid().ToString();
+
+ await _controller.EmailSentSuccessfully(userId, emailAddress);
+
+ _service.Verify(s => s.EmailSucceededToSend(userId, emailAddress), Times.Once);
+ }
+
+ [Test]
+ public async Task EmailSentSuccessfully_KnownEmail_ReturnsOk()
+ {
+ var userId = Guid.NewGuid().ToString();
+ var emailAddress = Guid.NewGuid().ToString();
+ _service
+ .Setup(s => s.EmailSucceededToSend(userId, emailAddress))
+ .ReturnsAsync(true);
+
+ var result = await _controller.EmailSentSuccessfully(userId, emailAddress);
+
+ Assert.AreEqual(200, (result as IStatusCodeActionResult)?.StatusCode);
+ }
+
+ [Test]
+ public async Task EmailSentSuccessfully_UnknownEmail_ReturnsNotFound()
+ {
+ var userId = Guid.NewGuid().ToString();
+ var emailAddress = Guid.NewGuid().ToString();
+ _service
+ .Setup(s => s.EmailFailedToSend(userId, emailAddress, true))
+ .ReturnsAsync(false);
+
+ var result = await _controller.EmailSentSuccessfully(userId, emailAddress);
+
+ Assert.AreEqual(404, (result as IStatusCodeActionResult)?.StatusCode);
+ }
+
+ [Test]
+ public async Task EmailSendFailed_AllCases_CallsServiceEmailFailedToSend()
+ {
+ var userId = Guid.NewGuid().ToString();
+ var emailAddress = Guid.NewGuid().ToString();
+ _featureManager.Setup(f => f.IsEnabledAsync(It.IsAny())).ReturnsAsync(true);
+
+ await _controller.EmailSendFailed(userId, emailAddress);
+
+ _service.Verify(s => s.EmailFailedToSend(userId, emailAddress, true), Times.Once);
+ }
+
+ [Test]
+ public async Task EmailSendFailed_KnownEmail_ReturnsOk()
+ {
+ var userId = Guid.NewGuid().ToString();
+ var emailAddress = Guid.NewGuid().ToString();
+ _featureManager.Setup(f => f.IsEnabledAsync(It.IsAny())).ReturnsAsync(true);
+ _service
+ .Setup(s => s.EmailFailedToSend(userId, emailAddress, true))
+ .ReturnsAsync(true);
+
+ var result = await _controller.EmailSendFailed(userId, emailAddress);
+
+ Assert.AreEqual(200, (result as IStatusCodeActionResult)?.StatusCode);
+ }
+
+ [Test]
+ public async Task EmailSendFailed_UnknownEmail_ReturnsNotFound()
+ {
+ var userId = Guid.NewGuid().ToString();
+ var emailAddress = Guid.NewGuid().ToString();
+ _service
+ .Setup(s => s.EmailFailedToSend(userId, emailAddress, true))
+ .ReturnsAsync(false);
+
+ var result = await _controller.EmailSendFailed(userId, emailAddress);
+
+ Assert.AreEqual(404, (result as IStatusCodeActionResult)?.StatusCode);
+ }
+ }
+}
diff --git a/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Controllers/MemberOptInSettingsControllerTest.cs b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Controllers/MemberOptInSettingsControllerTest.cs
new file mode 100644
index 0000000..0c6ab1e
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Controllers/MemberOptInSettingsControllerTest.cs
@@ -0,0 +1,396 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using LCU.API.Internal.MemberPreferencesApi.Controllers;
+using LCU.API.Internal.MemberPreferencesApi.Services;
+using LCU.Library.Internal.MemberPreferencesApiModels.DataTransfer;
+using LCU.Library.Internal.MemberPreferencesApiModels.Entities;
+using LCU.Library.Internal.MemberPreferencesApiModels.Requests;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Moq;
+using NUnit.Framework;
+
+namespace LCU.API.Internal.MemberPreferencesApi.UnitTests.Controllers
+{
+ public class MemberOptInSettingsControllerTest
+ {
+ private MemberOptInSettingsController _controller;
+ private Mock _mockOptinService;
+ private Mock _mockMemberPreferenceService;
+
+ [SetUp]
+ public void Setup()
+ {
+ _mockOptinService = new Mock();
+ _mockMemberPreferenceService = new Mock();
+ _controller =
+ new MemberOptInSettingsController(_mockOptinService.Object, _mockMemberPreferenceService.Object);
+ }
+
+ [Test]
+ public async Task Search_OK()
+ {
+ _mockOptinService
+ .Setup(service => service.GetMemberOptInAgreementInformationByAccountId(123)).ReturnsAsync(
+ new List()
+ {
+ new MemberOptInAgreementInformation(new MemberOptInSettings()
+ {
+ Id = 1
+ },
+ null,
+ null,
+ false)
+ }).Verifiable();
+
+ var result = await _controller.GetAgreementsByAccountId(123);
+ Assert.NotNull(result);
+
+ var okResult = result as OkObjectResult;
+ Assert.NotNull(okResult);
+ Assert.AreEqual(200, okResult.StatusCode);
+
+ var okValue = okResult.Value as List;
+ Assert.NotNull(okValue);
+ Assert.IsNotEmpty(okValue);
+
+ _mockOptinService.Verify();
+ }
+
+ [Test]
+ public async Task Search_Missing()
+ {
+ _mockOptinService
+ .Setup(service => service.GetMemberOptInAgreementInformationByAccountId(123)).ReturnsAsync(
+ new List()).Verifiable();
+
+ var result = await _controller.GetAgreementsByAccountId(123);
+ Assert.NotNull(result);
+
+ var okResult = result as NotFoundObjectResult;
+ Assert.NotNull(okResult);
+ Assert.AreEqual(404, okResult.StatusCode);
+
+ _mockOptinService.Verify();
+ }
+
+ [Test]
+ public async Task Search_BadRequest()
+ {
+ var result = await _controller.GetAgreementsByAccountId(0);
+ Assert.NotNull(result);
+
+ var brResult = result as BadRequestObjectResult;
+ Assert.NotNull(brResult);
+ Assert.AreEqual(400, brResult.StatusCode);
+
+ _mockOptinService.Verify(x => x.GetMemberOptInAgreementInformationByAccountId(It.IsAny()), Times.Never);
+ }
+
+ [Test]
+ public async Task Search_Failure()
+ {
+ _mockOptinService
+ .Setup(service => service.GetMemberOptInAgreementInformationByAccountId(123))
+ .Throws(new Exception("Exception thrown from unit test.")).Verifiable();
+
+ var result = await _controller.GetAgreementsByAccountId(123);
+ Assert.NotNull(result);
+
+ var badResult = result as StatusCodeResult;
+ Assert.NotNull(badResult);
+ Assert.AreEqual(500, badResult.StatusCode);
+ _mockOptinService.Verify();
+ }
+
+ [Test]
+ public async Task GetMemberOptinAgreementInformationByUserId_OK()
+ {
+ _mockOptinService
+ .Setup(service => service.GetMemberOptInAgreementInformationForUser("123")).ReturnsAsync(
+ new MemberOptInAgreementInformation(new MemberOptInSettings()
+ {
+ Id = 1
+ },
+ null,
+ null,
+ false)
+ ).Verifiable();
+
+ var result = await _controller.GetAgreementByUserId("123");
+ Assert.NotNull(result);
+
+ var okResult = result as OkObjectResult;
+ Assert.NotNull(okResult);
+ Assert.AreEqual(200, okResult.StatusCode);
+
+ var okValue = okResult.Value as MemberOptInAgreementInformation;
+ Assert.NotNull(okValue);
+ Assert.AreEqual(1 ,okValue.MemberOptinSettings.Id);
+
+ _mockOptinService.Verify();
+ }
+
+ [Test]
+ public async Task GetMemberOptinAgreementInformationByUserId_Missing()
+ {
+ _mockOptinService
+ .Setup(service => service.GetMemberOptInAgreementInformationForUser("123")).ReturnsAsync(
+ (MemberOptInAgreementInformation)null).Verifiable();
+
+ var result = await _controller.GetAgreementByUserId("123");
+ Assert.NotNull(result);
+
+ var okResult = result as NotFoundObjectResult;
+ Assert.NotNull(okResult);
+ Assert.AreEqual(404, okResult.StatusCode);
+
+ _mockOptinService.Verify();
+ }
+
+ [Test]
+ public async Task GetMemberOptinAgreementInformationByUserId_BadRequest()
+ {
+ var result = await _controller.GetAgreementByUserId(null);
+ Assert.NotNull(result);
+
+ var brResult = result as BadRequestObjectResult;
+ Assert.NotNull(brResult);
+ Assert.AreEqual(400, brResult.StatusCode);
+
+ result = await _controller.GetAgreementByUserId("");
+ Assert.NotNull(result);
+
+ brResult = result as BadRequestObjectResult;
+ Assert.NotNull(brResult);
+ Assert.AreEqual(400, brResult.StatusCode);
+
+ _mockOptinService.Verify(x => x.GetMemberOptInAgreementInformationForUser(It.IsAny()), Times.Never);
+ }
+
+ [Test]
+ public async Task GetMemberOptinAgreementInformationByUserId_Failure()
+ {
+ _mockOptinService
+ .Setup(service => service.GetMemberOptInAgreementInformationForUser("123"))
+ .Throws(new Exception("Exception thrown from unit test.")).Verifiable();
+
+ var result = await _controller.GetAgreementByUserId("123");
+ Assert.NotNull(result);
+
+ var badResult = result as StatusCodeResult;
+ Assert.NotNull(badResult);
+ Assert.AreEqual(500, badResult.StatusCode);
+ _mockOptinService.Verify();
+ }
+
+ [Test]
+ public async Task GetMember_OK()
+ {
+ _mockOptinService
+ .Setup(service => service.GetMemberOptInSettingsOrCreateDefaultSetting(It.IsAny())).ReturnsAsync(
+ new MemberOptInSettings()
+ {
+ Id = 1
+ }).Verifiable();
+
+ _mockOptinService
+ .Setup(x => x.TransformMemberOptInSettingsForClient(It.IsAny())).ReturnsAsync(
+ new MemberOptInInformation(new MemberOptInSettings {Id = 1},true)).Verifiable();
+
+ var result = await _controller.GetMemberById("99");
+ Assert.NotNull(result);
+
+ var okResult = result as OkObjectResult;
+ Assert.NotNull(okResult);
+ Assert.AreEqual(200, okResult.StatusCode);
+
+ var okValue = okResult.Value as MemberOptInInformation;
+ Assert.NotNull(okValue);
+ Assert.AreEqual(1, okValue.MemberOptinSettings.Id);
+
+ _mockOptinService.Verify();
+ _mockOptinService.Verify();
+ }
+
+ [Test]
+ public async Task GetMember_Missing()
+ {
+ _mockOptinService
+ .Setup(service => service.GetMemberOptInSettingsOrCreateDefaultSetting("99")).ReturnsAsync(
+ (MemberOptInSettings) null).Verifiable();
+
+ var result = await _controller.GetMemberById("99");
+ Assert.NotNull(result);
+
+ var okResult = result as NotFoundObjectResult;
+ Assert.NotNull(okResult);
+ Assert.AreEqual(404, okResult.StatusCode);
+
+ _mockOptinService.Verify();
+ }
+
+ [Test]
+ public async Task GetMember_Failure()
+ {
+ _mockOptinService
+ .Setup(service => service.GetMemberOptInSettingsOrCreateDefaultSetting("99"))
+ .Throws(new InvalidCastException()).Verifiable();
+
+ var result = await _controller.GetMemberById("99");
+ Assert.NotNull(result);
+
+ var badResult = result as StatusCodeResult;
+ Assert.NotNull(badResult);
+ Assert.AreEqual(500, badResult.StatusCode);
+
+ _mockOptinService.Verify();
+ _mockOptinService.Verify(x => x.TransformMemberOptInSettingsForClient(It.IsAny()), Times.Never);
+ }
+
+
+ [Test]
+ public async Task GetMember_Transform_Failure()
+ {
+ _mockOptinService
+ .Setup(service => service.GetMemberOptInSettingsOrCreateDefaultSetting("99")).ReturnsAsync(
+ new MemberOptInSettings()
+ {
+ Id = 1
+ }).Verifiable();
+
+ _mockOptinService
+ .Setup(x => x.TransformMemberOptInSettingsForClient(It.IsAny()))
+ .ThrowsAsync(new Exception("Oops")).Verifiable();
+
+ var result = await _controller.GetMemberById("99");
+ Assert.NotNull(result);
+
+ var badResult = result as StatusCodeResult;
+ Assert.NotNull(badResult);
+ Assert.AreEqual(500, badResult.StatusCode);
+
+ _mockOptinService.Verify();
+ _mockOptinService.Verify();
+ }
+
+ [Test]
+ public async Task Post_Email_OK()
+ {
+ var request = new MemberOptinRequest()
+ {
+ Value = "Email",
+ UserId = "99",
+ Email = "test@test.com",
+ LegalReference = "None"
+ };
+ _mockMemberPreferenceService
+ .Setup(service => service.UpdateMemberOptinAsync(It.IsAny()))
+ .Verifiable("Expected call to UpdateMemberOptinAsync");
+
+ var result = await _controller.UpdateOptinPreference(request, "test", "Associate");
+ Assert.NotNull(result);
+
+ var okResult = result as OkResult;
+ Assert.NotNull(okResult);
+ Assert.AreEqual(200, okResult.StatusCode);
+
+ _mockMemberPreferenceService.Verify(x => x.UpdateMemberOptinAsync(It.IsAny()),
+ Times.Once);
+ }
+
+ [Test]
+ public async Task Post_Bad_Request()
+ {
+ // arrange
+ var request = new MemberOptinRequest()
+ {
+ Value = null,
+ UserId = "99",
+ LegalReference = "None"
+ };
+ _controller.ModelState.AddModelError("Value", "The value field is required.");
+
+ // act
+ var result = await _controller.UpdateOptinPreference(request, "test", "Associate");
+ Assert.NotNull(result);
+ var badRequestResult = result as BadRequestObjectResult;
+
+ // assert
+ Assert.NotNull(badRequestResult);
+ Assert.AreEqual(StatusCodes.Status400BadRequest, badRequestResult.StatusCode);
+ }
+
+ [Test]
+ public async Task Post_Error()
+ {
+ // arrange
+ var request = new MemberOptinRequest()
+ {
+ Value = "True",
+ UserId = "99",
+ LegalReference = "None"
+ };
+
+ _mockMemberPreferenceService
+ .Setup(service => service.UpdateMemberOptinAsync(It.IsAny()))
+ .ThrowsAsync(new ArithmeticException());
+
+ // act
+ var result = await _controller.UpdateOptinPreference(request, "test", "Associate");
+ var badResult = result as StatusCodeResult;
+ Assert.NotNull(badResult);
+ Assert.AreEqual(500, badResult.StatusCode);
+
+ _mockMemberPreferenceService.Verify(x => x.UpdateMemberOptinAsync(It.IsAny()),
+ Times.Once);
+ }
+
+ [Test]
+ public async Task Post_Bad_Request_NoHeader()
+ {
+ // arrange
+ var request = new MemberOptinRequest()
+ {
+ Value = "Paper",
+ UserId = "99",
+ LegalReference = "None"
+ };
+
+ // act
+ var result = await _controller.UpdateOptinPreference(request, null, null);
+ Assert.NotNull(result);
+ var badRequestResult = result as BadRequestObjectResult;
+
+ // assert
+ Assert.NotNull(badRequestResult);
+ Assert.AreEqual(StatusCodes.Status400BadRequest, badRequestResult.StatusCode);
+ }
+
+ [Test]
+ public async Task Post_Bad_Request_NotValid()
+ {
+ // arrange
+ var request = new MemberOptinRequest()
+ {
+ Value = "Electronic",
+ UserId = "99",
+ LegalReference = "None"
+ };
+
+ _mockMemberPreferenceService
+ .Setup(service => service.UpdateMemberOptinAsync(It.IsAny()))
+ .ThrowsAsync(new Exception());
+
+ // act
+ var result = await _controller.UpdateOptinPreference(request, null, null);
+ Assert.NotNull(result);
+ var badRequestResult = result as BadRequestObjectResult;
+
+ // assert
+ Assert.NotNull(badRequestResult);
+ Assert.AreEqual(StatusCodes.Status400BadRequest, badRequestResult.StatusCode);
+ }
+ }
+}
diff --git a/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Controllers/MemberPreferenceControllerTest.cs b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Controllers/MemberPreferenceControllerTest.cs
new file mode 100644
index 0000000..ce563f6
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Controllers/MemberPreferenceControllerTest.cs
@@ -0,0 +1,241 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using LCU.API.Internal.MemberPreferencesApi.Services;
+using LCU.Library.Internal.MemberPreferencesApiModels.Entities;
+using LCU.Library.Internal.MemberPreferencesApiModels.Enums;
+using LCU.Library.Internal.MemberPreferencesApiModels;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Moq;
+using NUnit.Framework;
+using LCU.API.Internal.MemberPreferencesApi.Controllers;
+using LCU.Library.Internal.MemberPreferencesApiModels.Requests;
+
+namespace LCU.API.Internal.MemberPreferencesApi.UnitTests.Controllers
+{
+ public class MemberPreferenceControllerTest
+ {
+ private MemberPreferenceController _controller;
+ private Mock _mockMemberPreferenceService;
+
+ [SetUp]
+ public void Setup()
+ {
+ _mockMemberPreferenceService = new Mock();
+ _controller = new MemberPreferenceController(_mockMemberPreferenceService.Object);
+ }
+
+ [Test]
+ public async Task Get_OK()
+ {
+ _mockMemberPreferenceService
+ .Setup(service => service.GetMemberPreferencesAsync("99")).ReturnsAsync(
+ new List()
+ {
+ new MemberPreference()
+ {
+ Id = 1,
+ UserId = "99",
+ Preference = PreferenceType.ENotification,
+ Value = "true"
+ }
+ }).Verifiable();
+
+ var result = await _controller.Get("99");
+ Assert.NotNull(result);
+
+ var okResult = result as OkObjectResult;
+ Assert.NotNull(okResult);
+ Assert.AreEqual(200, okResult.StatusCode);
+
+ var okValue = okResult.Value as IReadOnlyCollection;
+ Assert.NotNull(okValue);
+ Assert.IsNotEmpty(okValue);
+ Assert.AreEqual(1, okValue.First().Id);
+ _mockMemberPreferenceService.Verify();
+ }
+
+ [Test]
+ public async Task Get_Missing()
+ {
+ _mockMemberPreferenceService
+ .Setup(service => service.GetMemberPreferencesAsync("99")).ReturnsAsync(
+ new List()).Verifiable();
+
+ var result = await _controller.Get("99");
+ Assert.NotNull(result);
+
+ var okResult = result as OkObjectResult;
+ Assert.NotNull(okResult);
+ Assert.AreEqual(200, okResult.StatusCode);
+
+ var okValue = okResult.Value as IReadOnlyCollection;
+ Assert.NotNull(okValue);
+ Assert.IsEmpty(okValue);
+ _mockMemberPreferenceService.Verify();
+ }
+
+ [Test]
+ public async Task Get_Failure()
+ {
+ _mockMemberPreferenceService
+ .Setup(service => service.GetMemberPreferencesAsync("99"))
+ .Throws(new InvalidCastException()).Verifiable();
+
+ var result = await _controller.Get("99");
+ Assert.NotNull(result);
+
+ var badResult = result as StatusCodeResult;
+ Assert.NotNull(badResult);
+ Assert.AreEqual(500, badResult.StatusCode);
+
+ _mockMemberPreferenceService.Verify();
+ }
+
+ [Test]
+ public void Get_Types_OK()
+ {
+ IReadOnlyCollection values = Enum.GetValues(typeof(PreferenceType))
+ .Cast()
+ .Select(v => v.ToString())
+ .ToList();
+
+ _mockMemberPreferenceService
+ .Setup(service => service.GetAvailableTypes()).Returns(values);
+
+ var result = _controller.GetAvailableTypes();
+ Assert.NotNull(result);
+
+ var okResult = result as OkObjectResult;
+ Assert.NotNull(okResult);
+ Assert.AreEqual(200, okResult.StatusCode);
+
+ var okValue = okResult.Value as IReadOnlyCollection;
+ Assert.NotNull(okValue);
+ Assert.IsNotEmpty(okValue);
+ }
+
+ [Test]
+ public async Task Post_OK()
+ {
+ var results = new MemberOptInSettings();
+ var request = new MemberPreferenceRequest
+ {
+ Preference = PreferenceType.ENotification,
+ Value = "True",
+ UserId = "99",
+ LegalReference = "None"
+ };
+ _mockMemberPreferenceService
+ .Setup(service => service.SaveMemberPreferenceAsync(It.IsAny()))
+ .ReturnsAsync(results);
+
+ var result = await _controller.CreateMemberPreference(request, "test","Associate");
+ Assert.NotNull(result);
+
+ var okResult = result as CreatedAtActionResult;
+ Assert.NotNull(okResult);
+ Assert.AreEqual(201, okResult.StatusCode);
+
+ Assert.AreEqual("99", okResult.RouteValues["userId"]);
+ _mockMemberPreferenceService.Verify(x => x.SaveMemberPreferenceAsync(It.IsAny()),
+ Times.Once);
+ }
+
+ [Test]
+ public async Task Post_Bad_Request()
+ {
+ // arrange
+ var request = new MemberPreferenceRequest()
+ {
+ Preference = PreferenceType.ENotification,
+ Value = null,
+ UserId = "99",
+ LegalReference = "None"
+ };
+ _controller.ModelState.AddModelError("Value", "The value field is required.");
+
+ // act
+ var result = await _controller.CreateMemberPreference(request, "test", "Associate");
+ Assert.NotNull(result);
+ var badRequestResult = result as BadRequestObjectResult;
+
+ // assert
+ Assert.NotNull(badRequestResult);
+ Assert.AreEqual(StatusCodes.Status400BadRequest, badRequestResult.StatusCode);
+ }
+
+ [Test]
+ public async Task Post_Error()
+ {
+ // arrange
+ var request = new MemberPreferenceRequest()
+ {
+ Preference = PreferenceType.ENotification,
+ Value = "True",
+ UserId = "99",
+ LegalReference = "None"
+ };
+
+ _mockMemberPreferenceService
+ .Setup(service => service.SaveMemberPreferenceAsync(It.IsAny()))
+ .ThrowsAsync(new ArithmeticException());
+
+ // act
+ var result = await _controller.CreateMemberPreference(request, "test", "Associate");
+ var badResult = result as StatusCodeResult;
+ Assert.NotNull(badResult);
+ Assert.AreEqual(500, badResult.StatusCode);
+
+ _mockMemberPreferenceService.Verify(x => x.SaveMemberPreferenceAsync(It.IsAny()),
+ Times.Once);
+ }
+
+ [Test]
+ public async Task Post_Member_Has_Hold()
+ {
+ var request = new MemberPreferenceRequest()
+ {
+ Preference = PreferenceType.ENotification,
+ Value = "True",
+ UserId = "99",
+ LegalReference = "None"
+ };
+
+ _mockMemberPreferenceService
+ .Setup(service => service.SaveMemberPreferenceAsync(It.IsAny()))
+ .ThrowsAsync(new Exception());
+
+ var result = await _controller.CreateMemberPreference(request, "test", "Associate");
+ Assert.NotNull(result);
+
+ var objectResult = result as StatusCodeResult;
+ Assert.NotNull(objectResult);
+ Assert.AreEqual(StatusCodes.Status500InternalServerError, objectResult.StatusCode);
+ }
+
+ [Test]
+ public async Task Post_Bad_Request_NoHeaders()
+ {
+ // arrange
+ var request = new MemberPreferenceRequest()
+ {
+ Preference = PreferenceType.ENotification,
+ Value = "Electronic",
+ UserId = "99",
+ LegalReference = "None"
+ };
+
+ // act
+ var result = await _controller.CreateMemberPreference(request, null, null);
+ Assert.NotNull(result);
+ var badRequestResult = result as BadRequestObjectResult;
+
+ // assert
+ Assert.NotNull(badRequestResult);
+ Assert.AreEqual(StatusCodes.Status400BadRequest, badRequestResult.StatusCode);
+ }
+ }
+}
diff --git a/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/LCU.API.Internal.MemberPreferencesApi.UnitTests.csproj b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/LCU.API.Internal.MemberPreferencesApi.UnitTests.csproj
new file mode 100644
index 0000000..a1d18c4
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/LCU.API.Internal.MemberPreferencesApi.UnitTests.csproj
@@ -0,0 +1,25 @@
+
+
+
+ net8.0
+
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Models/Entities/MemberOptInSettingsTests.cs b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Models/Entities/MemberOptInSettingsTests.cs
new file mode 100644
index 0000000..4d41406
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Models/Entities/MemberOptInSettingsTests.cs
@@ -0,0 +1,395 @@
+using System;
+using System.Linq;
+using LCU.Library.Internal.MemberPreferencesApiModels.Entities;
+using LCU.Library.Internal.MemberPreferencesApiModels.Enums;
+using NUnit.Framework;
+
+namespace LCU.Library.Internal.MemberPreferencesApiModels.UnitTests.Entities
+{
+ public class MemberOptInSettingsTests
+ {
+ private MemberOptInSettings _settings;
+
+ [SetUp]
+ public void SetUp()
+ {
+ _settings = new MemberOptInSettings();
+ }
+
+ [Test]
+ public void Apply_UnifiedOptIn_SetsUniversalOptInAndLegalVersionAndLegalDate()
+ {
+ _settings.UniversalOptIn = Guid.NewGuid().ToString();
+ _settings.LegalVersion = Guid.NewGuid().ToString();
+ _settings.LegalDate = DateTime.MinValue;
+ var preference = new MemberPreference
+ {
+ Preference = PreferenceType.UnifiedOptIn,
+ Value = Guid.NewGuid().ToString(),
+ LegalReference = Guid.NewGuid().ToString()
+ };
+ var actionStart = DateTime.Now;
+
+ _settings.Apply(preference);
+
+ var elapsedActionTime = DateTime.Now - actionStart;
+ Assert.AreEqual(preference.Value, _settings.UniversalOptIn);
+ Assert.AreEqual(preference.LegalReference, _settings.LegalVersion);
+ Assert.That(actionStart, Is.EqualTo(_settings.LegalDate).Within(elapsedActionTime));
+ }
+
+ [Test]
+ public void Apply_UnifiedOptInUnchanged_DoesNotClearEmailTracking()
+ {
+ var originalStatus = _settings.UnifiedOptInEmailStatus = RandomEnumValueFactory();
+ var originalFirstSent = _settings.UnifiedOptInEmailFirstSent = DateTime.Now.AddHours(-1);
+ var originalLastSent = _settings.UnifiedOptInEmailLastSent = DateTime.Now;
+ var originalCount = _settings.UnifiedOptInEmailStatusCount = DateTime.Now.Millisecond;
+ var preference = new MemberPreference
+ {
+ Preference = PreferenceType.UnifiedOptIn,
+ Value = _settings.UniversalOptIn = Guid.NewGuid().ToString(),
+ };
+
+ _settings.Apply(preference);
+
+ Assert.AreEqual(originalStatus, _settings.UnifiedOptInEmailStatus);
+ Assert.AreEqual(originalFirstSent, _settings.UnifiedOptInEmailFirstSent);
+ Assert.AreEqual(originalLastSent, _settings.UnifiedOptInEmailLastSent);
+ Assert.AreEqual(originalCount, _settings.UnifiedOptInEmailStatusCount);
+ }
+
+ [Test]
+ public void Apply_UnifiedOptInChanged_ClearsEmailTracking()
+ {
+ _settings.UniversalOptIn = Guid.NewGuid().ToString();
+ _settings.UnifiedOptInEmailStatus = RandomEnumValueFactory();
+ _settings.UnifiedOptInEmailFirstSent = DateTime.Now.AddHours(-1);
+ _settings.UnifiedOptInEmailLastSent = DateTime.Now;
+ _settings.UnifiedOptInEmailStatusCount = DateTime.Now.Millisecond;
+ var preference = new MemberPreference
+ {
+ Preference = PreferenceType.UnifiedOptIn,
+ Value = Guid.NewGuid().ToString(),
+ };
+
+ _settings.Apply(preference);
+
+ Assert.IsNull(_settings.UnifiedOptInEmailStatus);
+ Assert.IsNull(_settings.UnifiedOptInEmailFirstSent);
+ Assert.IsNull(_settings.UnifiedOptInEmailLastSent);
+ Assert.AreEqual(0, _settings.UnifiedOptInEmailStatusCount);
+ }
+
+ [Test]
+ public void Apply_UnifiedOptInEmail_SetsUnifiedOptInEmail()
+ {
+ _settings.UnifiedOptInEmail = Guid.NewGuid().ToString();
+ var preference = new MemberPreference
+ {
+ Preference = PreferenceType.UnifiedOptInEmail,
+ Value = Guid.NewGuid().ToString(),
+ };
+
+ _settings.Apply(preference);
+
+ Assert.AreEqual(preference.Value, _settings.UnifiedOptInEmail);
+ }
+
+ [Test]
+ public void Apply_UnifiedOptInEmailUnchanged_ClearsEmailTracking()
+ {
+ var originalStatus = _settings.UnifiedOptInEmailStatus = RandomEnumValueFactory();
+ var originalFirstSent = _settings.UnifiedOptInEmailFirstSent = DateTime.Now.AddHours(-1);
+ var originalLastSent = _settings.UnifiedOptInEmailLastSent = DateTime.Now;
+ var originalCount = _settings.UnifiedOptInEmailStatusCount = DateTime.Now.Millisecond;
+ var preference = new MemberPreference
+ {
+ Preference = PreferenceType.UnifiedOptInEmail,
+ Value = _settings.UnifiedOptInEmail = Guid.NewGuid().ToString(),
+ };
+
+ _settings.Apply(preference);
+
+ Assert.IsNull(_settings.UnifiedOptInEmailStatus);
+ Assert.IsNull(_settings.UnifiedOptInEmailFirstSent);
+ Assert.IsNull(_settings.UnifiedOptInEmailLastSent);
+ Assert.AreEqual(0, _settings.UnifiedOptInEmailStatusCount);
+ }
+
+ [Test]
+ public void Apply_UnifiedOptInEmailChanged_ClearsEmailTracking()
+ {
+ _settings.UnifiedOptInEmail = Guid.NewGuid().ToString();
+ _settings.UnifiedOptInEmailStatus = RandomEnumValueFactory();
+ _settings.UnifiedOptInEmailFirstSent = DateTime.Now.AddHours(-1);
+ _settings.UnifiedOptInEmailLastSent = DateTime.Now;
+ _settings.UnifiedOptInEmailStatusCount = DateTime.Now.Millisecond;
+ var preference = new MemberPreference
+ {
+ Preference = PreferenceType.UnifiedOptInEmail,
+ Value = Guid.NewGuid().ToString(),
+ };
+
+ _settings.Apply(preference);
+
+ Assert.IsNull(_settings.UnifiedOptInEmailStatus);
+ Assert.IsNull(_settings.UnifiedOptInEmailFirstSent);
+ Assert.IsNull(_settings.UnifiedOptInEmailLastSent);
+ Assert.AreEqual(0, _settings.UnifiedOptInEmailStatusCount);
+ }
+
+ [Test]
+ public void Apply_ENotification_SetsENotifications()
+ {
+ _settings.ENotifications = Guid.NewGuid().ToString();
+ var preference = new MemberPreference
+ {
+ Preference = PreferenceType.ENotification,
+ Value = Guid.NewGuid().ToString()
+ };
+
+ _settings.Apply(preference);
+
+ Assert.AreEqual(preference.Value, _settings.ENotifications);
+ }
+
+ [Test]
+ public void Apply_ENotificationsUnchanged_DoesNotClearEmailTracking()
+ {
+ var originalStatus = _settings.ENotificationsEmailStatus = RandomEnumValueFactory();
+ var originalFirstSent = _settings.ENotificationsEmailFirstSent = DateTime.Now.AddHours(-1);
+ var originalLastSent = _settings.ENotificationsEmailLastSent = DateTime.Now;
+ var originalCount = _settings.ENotificationsEmailStatusCount = DateTime.Now.Millisecond;
+ var preference = new MemberPreference
+ {
+ Preference = PreferenceType.ENotification,
+ Value = _settings.ENotifications = Guid.NewGuid().ToString(),
+ };
+
+ _settings.Apply(preference);
+
+ Assert.AreEqual(originalStatus, _settings.ENotificationsEmailStatus);
+ Assert.AreEqual(originalFirstSent, _settings.ENotificationsEmailFirstSent);
+ Assert.AreEqual(originalLastSent, _settings.ENotificationsEmailLastSent);
+ Assert.AreEqual(originalCount, _settings.ENotificationsEmailStatusCount);
+ }
+
+ [Test]
+ public void Apply_ENotificationsChanged_ClearsEmailTracking()
+ {
+ _settings.ENotifications = Guid.NewGuid().ToString();
+ _settings.ENotificationsEmailStatus = RandomEnumValueFactory();
+ _settings.ENotificationsEmailFirstSent = DateTime.Now.AddHours(-1);
+ _settings.ENotificationsEmailLastSent = DateTime.Now;
+ _settings.ENotificationsEmailStatusCount = DateTime.Now.Millisecond;
+ var preference = new MemberPreference
+ {
+ Preference = PreferenceType.ENotification,
+ Value = Guid.NewGuid().ToString(),
+ };
+
+ _settings.Apply(preference);
+
+ Assert.IsNull(_settings.ENotificationsEmailStatus);
+ Assert.IsNull(_settings.ENotificationsEmailFirstSent);
+ Assert.IsNull(_settings.ENotificationsEmailLastSent);
+ Assert.AreEqual(0, _settings.ENotificationsEmailStatusCount);
+ }
+
+ [Test]
+ public void Apply_ENotificationsEmail_SetsENotificationsEmail()
+ {
+ _settings.ENotificationsEmail = Guid.NewGuid().ToString();
+ var preference = new MemberPreference
+ {
+ Preference = PreferenceType.ENotificationsEmail,
+ Value = Guid.NewGuid().ToString(),
+ };
+
+ _settings.Apply(preference);
+
+ Assert.AreEqual(preference.Value, _settings.ENotificationsEmail);
+ }
+
+ [Test]
+ public void Apply_ENotificationsEmailUnchanged_ClearsEmailTracking()
+ {
+ var originalStatus = _settings.ENotificationsEmailStatus = RandomEnumValueFactory();
+ var originalFirstSent = _settings.ENotificationsEmailFirstSent = DateTime.Now.AddHours(-1);
+ var originalLastSent = _settings.ENotificationsEmailLastSent = DateTime.Now;
+ var originalCount = _settings.ENotificationsEmailStatusCount = DateTime.Now.Millisecond;
+ var preference = new MemberPreference
+ {
+ Preference = PreferenceType.ENotificationsEmail,
+ Value = _settings.ENotificationsEmail = Guid.NewGuid().ToString(),
+ };
+
+ _settings.Apply(preference);
+
+ Assert.IsNull(_settings.ENotificationsEmailStatus);
+ Assert.IsNull(_settings.ENotificationsEmailFirstSent);
+ Assert.IsNull(_settings.ENotificationsEmailLastSent);
+ Assert.AreEqual(0, _settings.ENotificationsEmailStatusCount);
+ }
+
+ [Test]
+ public void Apply_ENotificationsEmailChanged_ClearsEmailTracking()
+ {
+ _settings.ENotificationsEmail = Guid.NewGuid().ToString();
+ _settings.ENotificationsEmailStatus = RandomEnumValueFactory();
+ _settings.ENotificationsEmailFirstSent = DateTime.Now.AddHours(-1);
+ _settings.ENotificationsEmailLastSent = DateTime.Now;
+ _settings.ENotificationsEmailStatusCount = DateTime.Now.Millisecond;
+ var preference = new MemberPreference
+ {
+ Preference = PreferenceType.ENotificationsEmail,
+ Value = Guid.NewGuid().ToString(),
+ };
+
+ _settings.Apply(preference);
+
+ Assert.IsNull(_settings.ENotificationsEmailStatus);
+ Assert.IsNull(_settings.ENotificationsEmailFirstSent);
+ Assert.IsNull(_settings.ENotificationsEmailLastSent);
+ Assert.AreEqual(0, _settings.ENotificationsEmailStatusCount);
+ }
+
+ [Test]
+ public void Apply_EStatements_SetsEStatements()
+ {
+ _settings.EStatements = Guid.NewGuid().ToString();
+ var preference = new MemberPreference
+ {
+ Preference = PreferenceType.EStatements,
+ Value = Guid.NewGuid().ToString()
+ };
+
+ _settings.Apply(preference);
+
+ Assert.AreEqual(preference.Value, _settings.EStatements);
+ }
+
+ [Test]
+ public void Apply_EStatementsUnchanged_DoesNotClearEmailTracking()
+ {
+ var originalStatus = _settings.EStatementsEmailStatus = RandomEnumValueFactory();
+ var originalFirstSent = _settings.EStatementsEmailFirstSent = DateTime.Now.AddHours(-1);
+ var originalLastSent = _settings.EStatementsEmailLastSent = DateTime.Now;
+ var originalCount = _settings.EStatementsEmailStatusCount = DateTime.Now.Millisecond;
+ var preference = new MemberPreference
+ {
+ Preference = PreferenceType.EStatements,
+ Value = _settings.EStatements = Guid.NewGuid().ToString(),
+ };
+
+ _settings.Apply(preference);
+
+ Assert.AreEqual(originalStatus, _settings.EStatementsEmailStatus);
+ Assert.AreEqual(originalFirstSent, _settings.EStatementsEmailFirstSent);
+ Assert.AreEqual(originalLastSent, _settings.EStatementsEmailLastSent);
+ Assert.AreEqual(originalCount, _settings.EStatementsEmailStatusCount);
+ }
+
+ [Test]
+ public void Apply_EStatementsChanged_ClearsEmailTracking()
+ {
+ _settings.EStatements = Guid.NewGuid().ToString();
+ _settings.EStatementsEmailStatus = RandomEnumValueFactory();
+ _settings.EStatementsEmailFirstSent = DateTime.Now.AddHours(-1);
+ _settings.EStatementsEmailLastSent = DateTime.Now;
+ _settings.EStatementsEmailStatusCount = DateTime.Now.Millisecond;
+ var preference = new MemberPreference
+ {
+ Preference = PreferenceType.EStatements,
+ Value = Guid.NewGuid().ToString(),
+ };
+
+ _settings.Apply(preference);
+
+ Assert.IsNull(_settings.EStatementsEmailStatus);
+ Assert.IsNull(_settings.EStatementsEmailFirstSent);
+ Assert.IsNull(_settings.EStatementsEmailLastSent);
+ Assert.AreEqual(0, _settings.EStatementsEmailStatusCount);
+ }
+
+ [Test]
+ public void Apply_EStatementsEmail_SetsEStatementsEmail()
+ {
+ _settings.EStatementsEmail = Guid.NewGuid().ToString();
+ var preference = new MemberPreference
+ {
+ Preference = PreferenceType.EStatementsEmail,
+ Value = Guid.NewGuid().ToString(),
+ };
+
+ _settings.Apply(preference);
+
+ Assert.AreEqual(preference.Value, _settings.EStatementsEmail);
+ }
+
+ [Test]
+ public void Apply_EStatementsEmailUnchanged_ClearsEmailTracking()
+ {
+ var originalStatus = _settings.EStatementsEmailStatus = RandomEnumValueFactory();
+ var originalFirstSent = _settings.EStatementsEmailFirstSent = DateTime.Now.AddHours(-1);
+ var originalLastSent = _settings.EStatementsEmailLastSent = DateTime.Now;
+ var originalCount = _settings.EStatementsEmailStatusCount = DateTime.Now.Millisecond;
+ var preference = new MemberPreference
+ {
+ Preference = PreferenceType.EStatementsEmail,
+ Value = _settings.EStatementsEmail = Guid.NewGuid().ToString(),
+ };
+
+ _settings.Apply(preference);
+
+ Assert.IsNull(_settings.EStatementsEmailStatus);
+ Assert.IsNull(_settings.EStatementsEmailFirstSent);
+ Assert.IsNull(_settings.EStatementsEmailLastSent);
+ Assert.AreEqual(0, _settings.EStatementsEmailStatusCount);
+ }
+
+ [Test]
+ public void Apply_EStatementsEmailChanged_ClearsEmailTracking()
+ {
+ _settings.EStatementsEmail = Guid.NewGuid().ToString();
+ _settings.EStatementsEmailStatus = RandomEnumValueFactory();
+ _settings.EStatementsEmailFirstSent = DateTime.Now.AddHours(-1);
+ _settings.EStatementsEmailLastSent = DateTime.Now;
+ _settings.EStatementsEmailStatusCount = DateTime.Now.Millisecond;
+ var preference = new MemberPreference
+ {
+ Preference = PreferenceType.EStatementsEmail,
+ Value = Guid.NewGuid().ToString(),
+ };
+
+ _settings.Apply(preference);
+
+ Assert.IsNull(_settings.EStatementsEmailStatus);
+ Assert.IsNull(_settings.EStatementsEmailFirstSent);
+ Assert.IsNull(_settings.EStatementsEmailLastSent);
+ Assert.AreEqual(0, _settings.EStatementsEmailStatusCount);
+ }
+
+ [Test]
+ public void Apply_UnknownPreference_ThrowsException()
+ {
+ var preference = new MemberPreference
+ {
+ Preference = (PreferenceType)(Enum.GetValues(typeof(PreferenceType)).Length + 1)
+ };
+
+ var thrown = Assert.Throws(() => _settings.Apply(preference));
+
+ StringAssert.Contains(preference.Preference.ToString(), thrown?.Message);
+ }
+
+ private static T RandomEnumValueFactory()
+ where T : Enum
+ {
+ var values = Enum.GetValues(typeof(T)).Cast().ToArray();
+
+ return values[DateTime.Now.Millisecond % values.Length];
+ }
+ }
+}
diff --git a/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Models/Policies/Email/FailedToSend/EStatementsSixtyDaysUndeliverableEmailPolicyTest.cs b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Models/Policies/Email/FailedToSend/EStatementsSixtyDaysUndeliverableEmailPolicyTest.cs
new file mode 100644
index 0000000..4cfef35
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Models/Policies/Email/FailedToSend/EStatementsSixtyDaysUndeliverableEmailPolicyTest.cs
@@ -0,0 +1,28 @@
+using System;
+using LCU.API.Internal.MemberPreferencesApi.Models.EmailPolicies;
+using LCU.Library.Internal.MemberPreferencesApiModels.Entities;
+using LCU.Library.Internal.MemberPreferencesApiModels.Enums;
+
+namespace LCU.Library.Internal.MemberPreferencesApiModels.UnitTests.Policies.Email.FailedToSend
+{
+ public class EStatementsSixtyDaysUndeliverableEmailPolicyTest : SixtyDaysUndeliverableEmailPolicyTest
+ {
+ protected override SixtyDaysUndeliverableEmailPolicy Policy { get; }
+
+ public EStatementsSixtyDaysUndeliverableEmailPolicyTest()
+ {
+ Policy = new EStatementsSixtyDaysUndeliverableEmailPolicy(AuditableChanges, MemberPreferences);
+ }
+
+ protected override void SetEmailStatus(MemberOptInSettings settings, EmailStatus status)
+ => settings.EStatementsEmailStatus = status;
+ protected override void SetFirstSent(MemberOptInSettings settings, DateTime firstSent)
+ => settings.EStatementsEmailFirstSent = firstSent;
+ protected override void SetLastSend(MemberOptInSettings settings, DateTime lastSent)
+ => settings.EStatementsEmailLastSent = lastSent;
+ protected override void SetEligibleForPreferenceFlip(MemberOptInSettings settings)
+ => settings.UniversalOptIn = null;
+ protected override string GetPreference(MemberOptInSettings settings)
+ => settings.UniversalOptIn;
+ }
+}
diff --git a/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Models/Policies/Email/FailedToSend/SixtyDaysUndeliverableEmailPolicyTest.cs b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Models/Policies/Email/FailedToSend/SixtyDaysUndeliverableEmailPolicyTest.cs
new file mode 100644
index 0000000..b797123
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Models/Policies/Email/FailedToSend/SixtyDaysUndeliverableEmailPolicyTest.cs
@@ -0,0 +1,187 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using LCU.Library.Internal.MemberPreferencesApiModels.Entities;
+using LCU.Library.Internal.MemberPreferencesApiModels.Enums;
+using FluentAssertions;
+using NUnit.Framework;
+using LCU.Library.Internal.MemberPreferencesApiModels.Models;
+using LCU.API.Internal.MemberPreferencesApi.Models.EmailPolicies;
+
+namespace LCU.Library.Internal.MemberPreferencesApiModels.UnitTests.Policies.Email.FailedToSend
+{
+ public abstract class SixtyDaysUndeliverableEmailPolicyTest
+ {
+ protected abstract SixtyDaysUndeliverableEmailPolicy Policy { get; }
+ protected IAuditableChanges AuditableChanges => _auditableChanges;
+ protected IMemberPreferences MemberPreferences => _memberPreferences;
+
+ protected abstract void SetEmailStatus(MemberOptInSettings settings, EmailStatus status);
+ protected abstract void SetFirstSent(MemberOptInSettings settings, DateTime firstSent);
+ protected abstract void SetLastSend(MemberOptInSettings settings, DateTime lastSent);
+ protected abstract void SetEligibleForPreferenceFlip(MemberOptInSettings settings);
+ protected abstract string GetPreference(MemberOptInSettings settings);
+
+ private readonly FakeAuditableChanges _auditableChanges = new FakeAuditableChanges();
+ private readonly FakeMemberPreferences _memberPreferences = new FakeMemberPreferences();
+
+ [SetUp]
+ public void SetUp()
+ {
+ _memberPreferences.Changes.Clear();
+ }
+
+ [Test]
+ public async Task Apply_NotFailingDelivery_ReturnsFalse()
+ {
+ var settings = SettingsFactory();
+ SetEmailStatus(settings, EmailStatus.DeliveredSuccessfully);
+ var originalSettings = Clone(settings);
+ var anyChanges = await Policy.Apply(settings, true);
+
+ Assert.That(anyChanges, Is.False);
+ Assert.That(_memberPreferences.Changes.Any(), Is.False);
+ settings.Should().BeEquivalentTo(originalSettings);
+ }
+
+ [Test]
+ public async Task Apply_FailingDeliveryForLessThanSixtyDays_ReturnsFalse()
+ {
+ var settings = SettingsFactory();
+ SetEmailStatus(settings, EmailStatus.FailingDelivery);
+ SetFirstSent(settings, DateTime.Now.AddDays(-59));
+ SetLastSend(settings, DateTime.Now);
+ var originalSettings = Clone(settings);
+ var anyChanges = await Policy.Apply(settings, true);
+ Assert.That(anyChanges, Is.False);
+ Assert.That(_memberPreferences.Changes.Any(), Is.False);
+ settings.Should().BeEquivalentTo(originalSettings);
+ }
+
+ [Test]
+ public async Task Apply_FailingDeliveryForSixtyDaysOrMore_ReturnsTrueAndSetsToPaper()
+ {
+ var settings = SettingsFactory();
+ SetEmailStatus(settings, EmailStatus.FailingDelivery);
+ SetFirstSent(settings, DateTime.Now.AddDays(-60));
+ SetLastSend(settings, DateTime.Now);
+ SetEligibleForPreferenceFlip(settings);
+
+ var originalSettings = Clone(settings);
+ var anyChanges = await Policy.Apply(settings, true);
+
+ var preference = GetPreference(settings);
+ Assert.That(anyChanges, Is.True);
+ settings.Should().BeEquivalentTo
+ (
+ originalSettings,
+ except => except
+ .Excluding(s => s.LastUpdatedBy)
+ .Excluding(s => s.UniversalOptIn)
+ .Excluding(s => s.ENotifications)
+ .Excluding(s => s.EStatements)
+ );
+ Assert.That(preference, Is.EqualTo(DeliveryPreference.Paper));
+ Assert.That(settings.LastUpdatedBy, Is.EqualTo("BounceBackLimitReached"));
+ Assert.That(_memberPreferences.Changes.Count(p => p.Value == DeliveryPreference.Paper && p.UserId == settings.UserId && p.LastUpdatedBy == "BounceBackLimitReached"), Is.EqualTo(1));
+ }
+
+ [Test]
+ public async Task Apply_FailingDeliveryForSixtyDaysOrMore_FeatureFlagReturnsFalse()
+ {
+ var settings = SettingsFactory();
+ SetEmailStatus(settings, EmailStatus.FailingDelivery);
+ SetFirstSent(settings, DateTime.Now.AddDays(-60));
+ SetLastSend(settings, DateTime.Now);
+ var originalSettings = Clone(settings);
+ var anyChanges = await Policy.Apply(settings, false);
+ var preference = GetPreference(settings);
+
+ Assert.That(anyChanges, Is.False);
+ Assert.That(_memberPreferences.Changes.Any(), Is.False);
+ settings.Should().BeEquivalentTo(originalSettings);
+ Assert.That(_memberPreferences.Changes.Count(p => p.Value == DeliveryPreference.Paper && p.UserId == settings.UserId && p.LastUpdatedBy == "BounceBackLimitReached"), Is.EqualTo(0));
+ }
+
+ [Test]
+ public async Task Apply_FailingDeliveryForSixtyDaysOrMore_SetsTrackingToBounceBackSystem()
+ {
+ var settings = SettingsFactory();
+ SetEmailStatus(settings, EmailStatus.FailingDelivery);
+ SetFirstSent(settings, DateTime.Now.AddDays(-60));
+ SetLastSend(settings, DateTime.Now);
+ SetEligibleForPreferenceFlip(settings);
+ await Policy.Apply(settings, true);
+
+ Assert.That(settings.LastUpdatedBy, Is.EqualTo("BounceBackLimitReached"));
+ Assert.That(_auditableChanges.SystemModifiedAuditableEntities.ContainsKey(settings), Is.True);
+ Assert.That(_auditableChanges.SystemModifiedAuditableEntities[settings], Is.EqualTo("BounceBackLimitReached"));
+ }
+
+ private static MemberOptInSettings SettingsFactory()
+ => new MemberOptInSettings
+ {
+ LastUpdatedBy = Guid.NewGuid().ToString(),
+
+ UniversalOptIn = DeliveryPreference.Electronic,
+ UnifiedOptInEmailStatus = EmailStatus.DeliveredSuccessfully,
+ UnifiedOptInEmailFirstSent = DateTime.Now,
+ UnifiedOptInEmailLastSent = DateTime.Now,
+
+ ENotifications = DeliveryPreference.Electronic,
+ ENotificationsEmailStatus = EmailStatus.DeliveredSuccessfully,
+ ENotificationsEmailFirstSent = DateTime.Now,
+ ENotificationsEmailLastSent = DateTime.Now,
+
+ EStatements = DeliveryPreference.Electronic,
+ EStatementsEmailStatus = EmailStatus.DeliveredSuccessfully,
+ EStatementsEmailFirstSent = DateTime.Now,
+ EStatementsEmailLastSent = DateTime.Now
+ };
+
+ private static MemberOptInSettings Clone(MemberOptInSettings settings)
+ => new MemberOptInSettings
+ {
+ LastUpdatedBy = settings.LastUpdatedBy,
+
+ UniversalOptIn = settings.UniversalOptIn,
+ UnifiedOptInEmailStatus = settings.UnifiedOptInEmailStatus,
+ UnifiedOptInEmailFirstSent = settings.UnifiedOptInEmailFirstSent,
+ UnifiedOptInEmailLastSent = settings.UnifiedOptInEmailLastSent,
+
+ ENotifications = settings.ENotifications,
+ ENotificationsEmailStatus = settings.ENotificationsEmailStatus,
+ ENotificationsEmailFirstSent = settings.ENotificationsEmailFirstSent,
+ ENotificationsEmailLastSent = settings.ENotificationsEmailLastSent,
+
+ EStatements = settings.EStatements,
+ EStatementsEmailStatus = settings.EStatementsEmailStatus,
+ EStatementsEmailFirstSent = settings.EStatementsEmailFirstSent,
+ EStatementsEmailLastSent = settings.EStatementsEmailLastSent
+ };
+
+ private class FakeAuditableChanges : IAuditableChanges
+ {
+ public IDictionary SystemModifiedAuditableEntities { get; } =
+ new Dictionary();
+
+ public void SystemModified(Auditable entity, string modifiedBy)
+ {
+ SystemModifiedAuditableEntities[entity] = modifiedBy;
+ }
+ }
+
+ private class FakeMemberPreferences : IMemberPreferences
+ {
+ public IList Changes { get; }= new List();
+
+ public Task Add(MemberPreference preferenceChange)
+ {
+ Changes.Add(preferenceChange);
+
+ return Task.CompletedTask;
+ }
+ }
+ }
+}
diff --git a/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Models/Policies/Email/FailedToSend/UnifiedSixtyDaysUndeliverableEmailPolicyTest.cs b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Models/Policies/Email/FailedToSend/UnifiedSixtyDaysUndeliverableEmailPolicyTest.cs
new file mode 100644
index 0000000..b274f05
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Models/Policies/Email/FailedToSend/UnifiedSixtyDaysUndeliverableEmailPolicyTest.cs
@@ -0,0 +1,28 @@
+using System;
+using LCU.API.Internal.MemberPreferencesApi.Models.EmailPolicies;
+using LCU.Library.Internal.MemberPreferencesApiModels.Entities;
+using LCU.Library.Internal.MemberPreferencesApiModels.Enums;
+
+namespace LCU.Library.Internal.MemberPreferencesApiModels.UnitTests.Policies.Email.FailedToSend
+{
+ public class UnifiedSixtyDaysUndeliverableEmailPolicyTest : SixtyDaysUndeliverableEmailPolicyTest
+ {
+ protected override SixtyDaysUndeliverableEmailPolicy Policy { get; }
+
+ public UnifiedSixtyDaysUndeliverableEmailPolicyTest()
+ {
+ Policy= new UnifiedSixtyDaysUndeliverableEmailPolicy(AuditableChanges, MemberPreferences);
+ }
+
+ protected override void SetEmailStatus(MemberOptInSettings settings, EmailStatus status)
+ => settings.UnifiedOptInEmailStatus = status;
+ protected override void SetFirstSent(MemberOptInSettings settings, DateTime firstSent)
+ => settings.UnifiedOptInEmailFirstSent = firstSent;
+ protected override void SetLastSend(MemberOptInSettings settings, DateTime lastSent)
+ => settings.UnifiedOptInEmailLastSent = lastSent;
+ protected override void SetEligibleForPreferenceFlip(MemberOptInSettings settings)
+ => settings.UniversalOptIn = "Electronic";
+ protected override string GetPreference(MemberOptInSettings settings)
+ => settings.UniversalOptIn;
+ }
+}
diff --git a/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Repository/DnaAuditProcessRepositoryTest.cs b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Repository/DnaAuditProcessRepositoryTest.cs
new file mode 100644
index 0000000..26d62a5
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Repository/DnaAuditProcessRepositoryTest.cs
@@ -0,0 +1,76 @@
+using LCU.API.Internal.MemberPreferencesApi.Services;
+using LCU.Library.Internal.MemberPreferencesApiModels.Entities;
+using LCU.Library.Internal.MemberPreferencesApiModels.Enums;
+using Infrastructure.Services;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Moq;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using LCU.API.Internal.MemberPreferencesApiLib.Persistence;
+using LCU.API.Internal.MemberPreferencesApiLib.Repositories;
+
+namespace LCU.API.Internal.MemberPreferencesApi.UnitTests.Repository
+{
+ public class DnaAuditProcessRepositoryTest
+ {
+ private ApplicationDbContext _context;
+ private DnaAuditProcessRepository _repository;
+
+ [SetUp]
+ public void Setup()
+ {
+ var mockDateTime = new DateTimeService();
+ var options = new DbContextOptionsBuilder()
+ .UseInMemoryDatabase(Guid.NewGuid().ToString())
+ .EnableSensitiveDataLogging()
+ .Options;
+ _context = new ApplicationDbContext(options);
+ _repository = new DnaAuditProcessRepository(_context);
+ }
+
+ [Test]
+ public async Task Basic_Flow_Test()
+ {
+ var saved = await _repository.GetAllDnaAuditProcesses();
+ Assert.NotNull(saved);
+ Assert.IsEmpty(saved);
+
+ var dnaProcess = new DnaAuditProcess()
+ {
+ ProcessId = 1,
+ DnaProcessType = DnaAuditProcessType.DNAAccounts,
+ LastUpdated = DateTime.Now,
+ ConsecutiveFailures = 2
+ };
+ await _context.AddAsync(dnaProcess);
+ await _context.SaveChangesAsync();
+
+ saved = await _repository.GetAllDnaAuditProcesses();
+ Assert.NotNull(saved);
+ Assert.IsNotEmpty(saved);
+ var first = saved.First();
+
+ Assert.AreEqual(first.ConsecutiveFailures, 2);
+ Assert.AreEqual(first.DnaProcessType, DnaAuditProcessType.DNAAccounts);
+ Assert.AreEqual(first.ProcessId, 1);
+
+ dnaProcess.DnaProcessType = DnaAuditProcessType.DNAOrganization;
+ dnaProcess.ConsecutiveFailures = 0;
+ await _repository.Update(dnaProcess);
+
+ saved = await _repository.GetAllDnaAuditProcesses();
+ Assert.NotNull(saved);
+ Assert.IsNotEmpty(saved);
+ first = saved.First();
+
+ Assert.AreEqual(first.ConsecutiveFailures, 0);
+ Assert.AreEqual(first.DnaProcessType, DnaAuditProcessType.DNAOrganization);
+ Assert.AreEqual(first.ProcessId, 1);
+ }
+ }
+}
diff --git a/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Repository/MemberPreferenceRepositoryTest.cs b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Repository/MemberPreferenceRepositoryTest.cs
new file mode 100644
index 0000000..f0a7b37
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Repository/MemberPreferenceRepositoryTest.cs
@@ -0,0 +1,175 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using LCU.API.Internal.MemberPreferencesApi.Services;
+using LCU.Library.Internal.MemberPreferencesApiModels.Entities;
+using LCU.Library.Internal.MemberPreferencesApiModels.Enums;
+using Infrastructure.Services;
+using Microsoft.EntityFrameworkCore;
+using Moq;
+using NUnit.Framework;
+using LCU.API.Internal.MemberPreferencesApiLib.Persistence;
+using LCU.API.Internal.MemberPreferencesApiLib.Repositories;
+
+namespace LCU.API.Internal.MemberPreferencesApi.UnitTests.Repository
+{
+ public class MemberPreferenceRepositoryTest
+ {
+ private ApplicationDbContext _context;
+ private MemberPreferenceRepository _repository;
+ private UnifiedOptInRepository _optInRepository;
+
+ [SetUp]
+ public void Setup()
+ {
+ var mockDateTime = new DateTimeService();
+ var options = new DbContextOptionsBuilder()
+ .UseInMemoryDatabase(Guid.NewGuid().ToString())
+ .EnableSensitiveDataLogging()
+ .Options;
+ _context = new ApplicationDbContext(options);
+ _repository = new MemberPreferenceRepository(_context);
+ _optInRepository = new UnifiedOptInRepository(_context);
+ }
+
+ [Test]
+ public async Task Basic_Flow_Test()
+ {
+ var saved = await _repository.GetMemberPreferencesAsync("99");
+ Assert.NotNull(saved);
+ Assert.IsEmpty(saved);
+
+ await _repository.SaveMemberPreferenceAsync(new MemberPreference
+ {
+ UserId = "99",
+ Preference = PreferenceType.ENotification,
+ Value = "True"
+ });
+ await _context.SaveChangesAsync();
+
+ saved = await _repository.GetMemberPreferencesAsync("99");
+ Assert.NotNull(saved);
+ Assert.IsNotEmpty(saved);
+ var first = saved.First();
+ // Make sure our Auditable is working.
+ Assert.NotNull(first.CreatedBy);
+ Assert.NotNull(first.Created);
+ Assert.NotNull(first.LastUpdatedBy);
+ Assert.NotNull(first.LastUpdated);
+ }
+
+ [Test]
+ public async Task Bulk_Flow_Test()
+ {
+ var memberResult = await _optInRepository.GetMemberOptInSettings("99");
+ Assert.Null(memberResult);
+
+ var listOfPreferences = new List
+ {
+ new MemberPreference
+ {
+ UserId = "99",
+ Preference = PreferenceType.EStatements,
+ Value = "True"
+ },
+ new MemberPreference
+ {
+ UserId = "100",
+ Preference = PreferenceType.EStatements,
+ Value = "True"
+ },
+ new MemberPreference
+ {
+ UserId = "101",
+ Preference = PreferenceType.EStatements,
+ Value = "True"
+ }
+ };
+
+ await _repository.BulkSaveMemberPreferenceAsync(listOfPreferences);
+ await _context.SaveChangesAsync();
+
+ var saved = await _repository.GetMemberPreferencesAsync("99");
+ Assert.NotNull(saved);
+ Assert.IsNotEmpty(saved);
+ var first = saved.First();
+ // Make sure our Auditable is working.
+ Assert.NotNull(first.CreatedBy);
+ Assert.NotNull(first.Created);
+ Assert.NotNull(first.LastUpdatedBy);
+ Assert.NotNull(first.LastUpdated);
+
+ saved = await _repository.GetMemberPreferencesAsync("100");
+ Assert.NotNull(saved);
+ Assert.IsNotEmpty(saved);
+ first = saved.First();
+ // Make sure our Auditable is working.
+ Assert.NotNull(first.CreatedBy);
+ Assert.NotNull(first.Created);
+ Assert.NotNull(first.LastUpdatedBy);
+ Assert.NotNull(first.LastUpdated);
+
+ saved = await _repository.GetMemberPreferencesAsync("101");
+ Assert.NotNull(saved);
+ Assert.IsNotEmpty(saved);
+ first = saved.First();
+ // Make sure our Auditable is working.
+ Assert.NotNull(first.CreatedBy);
+ Assert.NotNull(first.Created);
+ Assert.NotNull(first.LastUpdatedBy);
+ Assert.NotNull(first.LastUpdated);
+
+ memberResult = await _optInRepository.GetMemberOptInSettings("99");
+ Assert.NotNull(memberResult);
+ Assert.AreEqual("True", memberResult.EStatements);
+
+ memberResult = await _optInRepository.GetMemberOptInSettings("100");
+ Assert.NotNull(memberResult);
+ Assert.AreEqual("True", memberResult.EStatements);
+
+ memberResult = await _optInRepository.GetMemberOptInSettings("101");
+ Assert.NotNull(memberResult);
+ Assert.AreEqual("True", memberResult.EStatements);
+
+ listOfPreferences = new List
+ {
+ new MemberPreference
+ {
+ UserId = "99",
+ Preference = PreferenceType.ENotification,
+ Value = "Yes"
+ },
+ new MemberPreference
+ {
+ UserId = "100",
+ Preference = PreferenceType.ENotification,
+ Value = "Yes"
+ },
+ new MemberPreference
+ {
+ UserId = "101",
+ Preference = PreferenceType.ENotification,
+ Value = "Yes"
+ }
+ };
+
+ await _repository.BulkSaveMemberPreferenceAsync(listOfPreferences);
+
+ memberResult = await _optInRepository.GetMemberOptInSettings("99");
+ Assert.AreEqual(1, saved.Count);
+ Assert.NotNull(memberResult);
+ Assert.AreEqual("Yes", memberResult.ENotifications);
+
+ memberResult = await _optInRepository.GetMemberOptInSettings("100");
+ Assert.AreEqual(1, saved.Count);
+ Assert.NotNull(memberResult);
+ Assert.AreEqual("Yes", memberResult.ENotifications);
+
+ memberResult = await _optInRepository.GetMemberOptInSettings("101");
+ Assert.AreEqual(1, saved.Count);
+ Assert.NotNull(memberResult);
+ Assert.AreEqual("Yes", memberResult.ENotifications);
+ }
+ }
+}
diff --git a/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Repository/UnifiedOptInRepositoryTest.cs b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Repository/UnifiedOptInRepositoryTest.cs
new file mode 100644
index 0000000..8193967
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Repository/UnifiedOptInRepositoryTest.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Threading.Tasks;
+using LCU.API.Internal.MemberPreferencesApi.Services;
+using LCU.Library.Internal.MemberPreferencesApiModels.Entities;
+using LCU.Library.Internal.MemberPreferencesApiModels.Enums;
+using Infrastructure.Services;
+using Microsoft.EntityFrameworkCore;
+using Moq;
+using NUnit.Framework;
+using LCU.API.Internal.MemberPreferencesApiLib.Persistence;
+using LCU.API.Internal.MemberPreferencesApiLib.Repositories;
+
+namespace LCU.API.Internal.MemberPreferencesApi.UnitTests.Repository
+{
+ public class UnifiedOptInRepositoryTest
+ {
+ private ApplicationDbContext _context;
+ private UnifiedOptInRepository _optInRepository;
+
+ [SetUp]
+ public void Setup()
+ {
+ var mockDateTime = new DateTimeService();
+ var options = new DbContextOptionsBuilder()
+ .UseInMemoryDatabase(Guid.NewGuid().ToString())
+ .EnableSensitiveDataLogging()
+ .Options;
+ _context = new ApplicationDbContext(options);
+ _optInRepository = new UnifiedOptInRepository(_context);
+ }
+
+ [Test]
+ public async Task Basic_Flow_Test()
+ {
+ var memberResult = await _optInRepository.GetMemberOptInSettings("99");
+ Assert.Null(memberResult);
+
+ memberResult = await _optInRepository.GetOrCreateMemberOptinSettings("99");
+ await _context.SaveChangesAsync();
+ Assert.NotNull(memberResult);
+
+ // Make sure our Auditable is working.
+ Assert.NotNull(memberResult.CreatedBy);
+ Assert.NotNull(memberResult.Created);
+ Assert.NotNull(memberResult.LastUpdatedBy);
+ Assert.NotNull(memberResult.LastUpdated);
+ }
+ }
+}
diff --git a/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Services/DNADataAuditProcessServiceTest.cs b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Services/DNADataAuditProcessServiceTest.cs
new file mode 100644
index 0000000..e1c7738
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Services/DNADataAuditProcessServiceTest.cs
@@ -0,0 +1,130 @@
+using Moq;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System.Linq;
+using LCU.Library.Internal.MemberPreferencesApiModels.Entities;
+using LCU.Library.Internal.MemberPreferencesApiModels.Enums;
+using LCU.API.Internal.MemberPreferencesApi.Services.QueueServices;
+using LCU.API.Internal.MemberPreferencesApi.Interfaces;
+using LCU.API.Internal.MemberPreferencesApi.Configuration;
+using LCU.API.Internal.MemberPreferencesApiLib.Repositories;
+using LCU.API.Internal.MemberPreferencesApi.Services.DnaAuditServices;
+using LCU.API.Internal.MemberPreferencesApi.MicroApiServices;
+
+namespace LCU.API.Internal.MemberPreferencesApi.UnitTests.Services
+{
+ public class DNADataAuditProcessServiceTest
+ {
+ private Mock _coreApiWrapperServiceMock;
+ private Mock _queueAccountMock;
+ private Mock _queueAgreementMock;
+ private Mock _queueOrganizationMock;
+ private Mock _dnaAuditProcessRepositoryMock;
+ private DNADataAuditProcessService _service;
+ private Mock _dateTimeMock;
+ private DnaAuditProcessConfiguration _config;
+
+ [SetUp]
+ public void Setup()
+ {
+ _dateTimeMock = new Mock();
+ _config = new DnaAuditProcessConfiguration();
+ _coreApiWrapperServiceMock = new Mock();
+ _queueAccountMock = new Mock();
+ _queueAgreementMock = new Mock();
+ _queueOrganizationMock = new Mock();
+ _dnaAuditProcessRepositoryMock = new Mock();
+ _service = new DNADataAuditProcessService(_queueAccountMock.Object, _queueAgreementMock.Object, _queueOrganizationMock.Object,
+ _coreApiWrapperServiceMock.Object, _dateTimeMock.Object, _dnaAuditProcessRepositoryMock.Object, _config);
+ }
+
+ [Test]
+ public async Task RunDnaAuditProcessesChange_HandleProcessUpdate_Ok()
+ {
+
+ var processlistAccount = new List
+ {
+ new DnaAuditProcess()
+ {
+ ConsecutiveFailures = 0,
+ DnaProcessType = DnaAuditProcessType.DNAAccounts,
+ LastUpdated = DateTime.Now,
+ ProcessId = 0,
+ }
+ };
+
+ var processlistAgreement = new List
+ {
+ new DnaAuditProcess()
+ {
+ ConsecutiveFailures = 0,
+ DnaProcessType = DnaAuditProcessType.DNAAgreement,
+ LastUpdated = DateTime.Now,
+ ProcessId = 0,
+ }
+ };
+
+ var processlistOrg = new List
+ {
+ new DnaAuditProcess()
+ {
+ ConsecutiveFailures = 0,
+ DnaProcessType = DnaAuditProcessType.DNAOrganization,
+ LastUpdated = DateTime.Now,
+ ProcessId = 0,
+ }
+ };
+
+ await _service.RunDataAuditProcesses();
+ await _service.AuditDNAAccountsForTimePeriod(DateTime.Now, processlistAccount.First());
+ var resultAccount = _service.HandleDnaAuditProcessSuccess(processlistAccount.First());
+
+ await _service.AuditDNAAgreementForTimePeriod(DateTime.Now, processlistAgreement.First());
+ var resultAgreement = _service.HandleDnaAuditProcessSuccess(processlistAgreement.First());
+
+ await _service.AuditDNAOrganizationForTimePeriod(DateTime.Now, processlistOrg.First());
+ var resultOrg = _service.HandleDnaAuditProcessSuccess(processlistOrg.First());
+
+ //assert
+ Assert.NotNull(resultAccount);
+ Assert.AreEqual(resultAccount.DnaProcessType, DnaAuditProcessType.DNAAccounts);
+ Assert.AreEqual(resultAccount.ConsecutiveFailures, 0);
+
+ Assert.NotNull(resultAgreement);
+ Assert.AreEqual(resultAgreement.DnaProcessType, DnaAuditProcessType.DNAAgreement);
+ Assert.AreEqual(resultAgreement.ConsecutiveFailures, 0);
+
+ Assert.NotNull(resultOrg);
+ Assert.AreEqual(resultOrg.DnaProcessType, DnaAuditProcessType.DNAOrganization);
+ Assert.AreEqual(resultOrg.ConsecutiveFailures, 0);
+ }
+
+ [Test]
+ public async Task RunDnaAuditProcessesChange_HandleProcessUpdateException_Ok()
+ {
+
+ var processlistAccount = new List
+ {
+ new DnaAuditProcess()
+ {
+ ConsecutiveFailures = 0,
+ DnaProcessType = DnaAuditProcessType.DNAAccounts,
+ LastUpdated = DateTime.Now,
+ ProcessId = 0,
+ }
+ };
+
+ var ex = new Exception();
+ await _service.RunDataAuditProcesses();
+ await _service.AuditDNAAccountsForTimePeriod(DateTime.Now, processlistAccount.First());
+ var resultAccount = _service.HandleDnaAuditProcessException(processlistAccount.First(), ex);
+
+ //assert
+ Assert.NotNull(resultAccount);
+ Assert.AreEqual(resultAccount.DnaProcessType, DnaAuditProcessType.DNAAccounts);
+ Assert.AreEqual(resultAccount.ConsecutiveFailures, 1);
+ }
+ }
+}
diff --git a/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Services/DNADataAuditServiceTest.cs b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Services/DNADataAuditServiceTest.cs
new file mode 100644
index 0000000..2ded22e
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Services/DNADataAuditServiceTest.cs
@@ -0,0 +1,223 @@
+using Moq;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using LCU.API.Internal.MemberPreferencesApi.Services;
+using LCU.Library.Internal.MemberPreferencesApiModels.Entities;
+using LCU.Library.Internal.MemberPreferencesApiModels.Enums;
+using LCU.Library.Internal.CoreApiWrapperModel.CoreModels.Agreements;
+using LCU.API.Internal.MemberPreferencesApi.Services.DnaAuditServices;
+using LCU.API.Internal.MemberPreferencesApi.MicroApiServices;
+using LCU.API.Internal.MemberPreferencesApi.Models.Requests;
+
+namespace LCU.API.Internal.MemberPreferencesApi.UnitTests.Services
+{
+ public class DNADataAuditServiceTest
+ {
+ private Mock _coreApiWrapperServiceMock;
+ private Mock _optinServiceMock;
+ private DNADataAuditService _service;
+
+ [SetUp]
+ public void Setup()
+ {
+ _coreApiWrapperServiceMock = new Mock();
+ _optinServiceMock = new Mock();
+ _service = new DNADataAuditService(_coreApiWrapperServiceMock.Object, _optinServiceMock.Object);
+ }
+
+ [Test]
+ public async Task HandleChangedFields_AccountChange_OK_UPDATE()
+ {
+
+ var account = new AgreementAccount
+ {
+ AccountNumber = 123456,
+ OvrDeliveryMethCD = "PRNT",
+ AccountOwner = new AgreementAccountOwner
+ {
+ AccountNumber = 123456,
+ AgreementNumber = 222,
+ PersonNumber = 123,
+ UserId = "1234"
+ }
+ };
+
+ var memberOptinSettings = new MemberOptInSettings
+ {
+ EStatements = null,
+ UniversalOptIn = "Electronic"
+ };
+
+ //setup
+ _coreApiWrapperServiceMock.Setup(x => x.GetAgreementAccountByAccountId(123456)).ReturnsAsync(account).Verifiable();
+
+ _optinServiceMock.Setup(x => x.GetMemberOptInSettings("1234")).ReturnsAsync(memberOptinSettings).Verifiable();
+
+ var request = new DnaFieldChangeRequest()
+ {
+ ConsecutiveFailures = 0,
+ DnaFieldIdInformation = 123456L,
+ DnaProcessType = DnaAuditProcessType.DNAAgreement,
+ LastUpdated = DateTime.Now,
+ ProcessId = 1
+ };
+
+ await _service.HandleAccountAuditCheck(request);
+
+ //assert
+ _coreApiWrapperServiceMock.Verify(x => x.UpdateAgreementAccountsDeliveryMethod(It.Is>(list => list.Contains(123456)), It.Is(x => x.Equals("WWW")), It.Is(x => x.Equals("1234"))), Times.Once);
+ _coreApiWrapperServiceMock.Verify();
+ _optinServiceMock.Verify();
+ }
+
+ [Test]
+ public async Task HandleChangedFields_AccountChange_OK_DONTUPDATE()
+ {
+ var account = new AgreementAccount
+ {
+ AccountNumber = 123456,
+ OvrDeliveryMethCD = "PRNT",
+ AccountOwner = new AgreementAccountOwner
+ {
+ AccountNumber = 123456,
+ AgreementNumber = 222,
+ PersonNumber = 123,
+ UserId = "1234"
+ }
+ };
+
+ var memberOptinSettings = new MemberOptInSettings
+ {
+ EStatements = null,
+ UniversalOptIn = "Paper"
+ };
+
+ //setup
+ _coreApiWrapperServiceMock.Setup(x => x.GetAgreementAccountByAccountId(123456)).ReturnsAsync(account).Verifiable();
+
+ _optinServiceMock.Setup(x => x.GetMemberOptInSettings("1234"))
+ .ReturnsAsync(memberOptinSettings).Verifiable();
+
+ var request = new DnaFieldChangeRequest()
+ {
+ ConsecutiveFailures = 0,
+ DnaFieldIdInformation = 123456L,
+ DnaProcessType = DnaAuditProcessType.DNAAccounts,
+ LastUpdated = DateTime.Now,
+ ProcessId = 1
+ };
+
+ await _service.HandleAccountAuditCheck(request);
+
+ //assert
+ _coreApiWrapperServiceMock.Verify(x => x.UpdateAgreementAccountsDeliveryMethod(It.IsAny>(), It.IsAny(), It.IsAny()), Times.Never);
+ _coreApiWrapperServiceMock.Verify();
+ _optinServiceMock.Verify();
+ }
+
+ [Test]
+ public async Task HandleChangedFields_AgreementChange_OK_UPDATE()
+ {
+ var agreement = new Agreement
+ {
+ AgreementNumber = 1127,
+ PersonNumber = 123,
+ UserId = "1234"
+ };
+
+ var accounts = new List
+ {
+ new AgreementAccount{
+ AccountNumber = 123456,
+ OvrDeliveryMethCD = "PRNT",
+ TaxReportForPersonNumber = 123,
+ AccountOwner = new AgreementAccountOwner{
+ AccountNumber = 123456,
+ AgreementNumber = 1127,
+ PersonNumber = 123,
+ UserId = "1234"
+ }
+ }
+ };
+
+ var memberOptinSettings = new MemberOptInSettings
+ {
+ EStatements = null,
+ UniversalOptIn = "Electronic"
+ };
+
+ //setup
+ _coreApiWrapperServiceMock.Setup(x => x.GetAgreementByAgreementId(1127)).ReturnsAsync(agreement).Verifiable();
+ _coreApiWrapperServiceMock.Setup(x => x.GetAgreementAccountsByUserId("1234")).ReturnsAsync(accounts).Verifiable();
+
+ _optinServiceMock.Setup(x => x.GetMemberOptInSettings("1234")).ReturnsAsync(memberOptinSettings).Verifiable();
+
+ var request = new DnaFieldChangeRequest()
+ {
+ ConsecutiveFailures = 0,
+ DnaFieldIdInformation = 1127L,
+ DnaProcessType = DnaAuditProcessType.DNAAgreement,
+ LastUpdated = DateTime.Now,
+ ProcessId = 1
+ };
+
+ await _service.HandleAgreementAuditCheck(request);
+
+ //assert
+ _coreApiWrapperServiceMock.Verify(x => x.UpdateAgreementAccountsDeliveryMethod(It.Is>(list => list.Contains(123456)), It.Is(x => x.Equals("WWW")), It.Is(x => x.Equals("1234"))), Times.Once);
+ _coreApiWrapperServiceMock.Verify();
+ _optinServiceMock.Verify();
+ }
+
+ [Test]
+ public async Task HandleChangedFields_OrganizationChange_OK_UPDATE()
+ {
+ var accounts = new List
+ {
+ new AgreementAccount{
+ AccountNumber = 123456,
+ TaxReportForOrgNumber = 11267,
+ OvrDeliveryMethCD = "PRNT",
+ AccountOwner = new AgreementAccountOwner
+ {
+ AccountNumber = 123456,
+ AgreementNumber = 222,
+ PersonNumber = 123,
+ UserId = "1234"
+ }
+ }
+ };
+
+ var memberOptinSettings = new MemberOptInSettings
+ {
+ EStatements = null,
+ UniversalOptIn = "Electronic"
+ };
+
+ //setup
+
+ _coreApiWrapperServiceMock.Setup(x => x.GetAgreementAccountsByOrganizationId(1126)).ReturnsAsync(accounts).Verifiable();
+
+ _optinServiceMock.Setup(x => x.GetMemberOptInSettings("1234")).ReturnsAsync(memberOptinSettings).Verifiable();
+
+ var request = new DnaFieldChangeRequest()
+ {
+ ConsecutiveFailures = 0,
+ DnaFieldIdInformation = 1126L,
+ DnaProcessType = DnaAuditProcessType.DNAAgreement,
+ LastUpdated = DateTime.Now,
+ ProcessId = 1
+ };
+
+ //act
+ await _service.HandleOrganizationAuditCheck(request);
+
+ //assert
+ _coreApiWrapperServiceMock.Verify(x => x.UpdateAgreementAccountsDeliveryMethod(It.Is>(list => list.Contains(123456)), It.Is(x => x.Equals("WWW")), It.Is(x => x.Equals("1234"))), Times.Once);
+ _coreApiWrapperServiceMock.Verify();
+ _optinServiceMock.Verify();
+ }
+ }
+}
diff --git a/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Services/MemberEmailServiceTest.cs b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Services/MemberEmailServiceTest.cs
new file mode 100644
index 0000000..52dc7f6
--- /dev/null
+++ b/LCU.API.Internal.MemberPreferencesApi/LCU.API.Internal.MemberPreferencesApi.UnitTests/Services/MemberEmailServiceTest.cs
@@ -0,0 +1,882 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using LCU.API.Internal.MemberPreferencesApi.Services;
+using LCU.API.Internal.MemberPreferencesApiLib.Repositories;
+using LCU.API.Internal.MemberPreferencesApi.Models.EmailPolicies;
+using LCU.Library.Internal.MemberPreferencesApiModels.Entities;
+using LCU.Library.Internal.MemberPreferencesApiModels.Enums;
+using Moq;
+using Newtonsoft.Json;
+using NUnit.Framework;
+
+namespace LCU.API.Internal.MemberPreferencesApi.UnitTests.Services
+{
+ public class MemberEmailServiceTest
+ {
+ private readonly IMemberEmailService _service;
+
+ private readonly FakeUnifiedOptInRepository _repository;
+
+ private readonly Mock _mockIntegrationService;
+
+ private readonly Mock _mockPreferenceService;
+
+ private readonly Mock[] _policies =
+ {
+ new Mock(),
+ new Mock()
+ };
+
+ public MemberEmailServiceTest()
+ {
+ var unitOfWork = new FakeUnitOfWork();
+ _repository = new FakeUnifiedOptInRepository(unitOfWork);
+ _mockIntegrationService = new Mock();
+ _mockPreferenceService = new Mock();
+
+ _service = new MemberEmailService(_repository, unitOfWork, _policies.Select(p => p.Object), _mockPreferenceService.Object, _mockIntegrationService.Object);
+ }
+
+ private static readonly (DateTime? firstSent, DateTime? lastSent, EmailStatus PriorStatus, int count)[]
+ PreUpdateStateScenarios =
+ {
+ (null, null, EmailStatus.FailingDelivery, 1),
+ (DateTime.Now.AddHours(-2), null, EmailStatus.FailingDelivery, 2),
+ (null, DateTime.Now.AddHours(-3), EmailStatus.FailingDelivery, 3),
+ (DateTime.Now.AddHours(-4), DateTime.Now.AddHours(-4), EmailStatus.FailingDelivery, 4),
+ (null, null, EmailStatus.DeliveredSuccessfully, 5),
+ (DateTime.Now.AddHours(-6), null, EmailStatus.DeliveredSuccessfully, 6),
+ (null, DateTime.Now.AddHours(-7), EmailStatus.DeliveredSuccessfully, 7),
+ (DateTime.Now.AddHours(-8), DateTime.Now.AddHours(-8), EmailStatus.DeliveredSuccessfully, 8),
+ };
+
+ private static readonly (bool isUnifiedOptIn, bool matchesUnifiedOptin, bool matchesENotification, bool matchesEstatement)[]
+ MatchScenarios =
+ {
+ (true, true, true, true),
+ (true, true, true, false),
+ (true, true, false, true),
+ (true, true, false, false),
+ (false, false, true, true),
+ (false, false, true, false),
+ (false, false, false, true),
+ (false, false, true, true),
+ (false, false, true, false),
+ (false, false, false, true),
+ };
+
+ private static IEnumerable