diff --git a/application/CohortManager/compose.cohort-distribution.yaml b/application/CohortManager/compose.cohort-distribution.yaml index 3345a47227..6df81a6b5c 100644 --- a/application/CohortManager/compose.cohort-distribution.yaml +++ b/application/CohortManager/compose.cohort-distribution.yaml @@ -44,6 +44,7 @@ services: - CohortDistributionDataServiceURL=http://cohort-distribution-data-service:7992/api/CohortDistributionDataService/ - BsSelectRequestAuditDataService=http://bs-request-audit-data-service:7989/api/BsSelectRequestAuditDataService/ - AcceptableLatencyThresholdMs=500 + - RetrieveSupersededRecordsLast=false retrieve-cohort-request-audit: container_name: retrieve-cohort-request-audit diff --git a/application/CohortManager/src/Functions/CohortDistributionServices/RetrieveCohortDistribution/Program.cs b/application/CohortManager/src/Functions/CohortDistributionServices/RetrieveCohortDistribution/Program.cs index 1c0056bd2d..0caa6fd7d5 100644 --- a/application/CohortManager/src/Functions/CohortDistributionServices/RetrieveCohortDistribution/Program.cs +++ b/application/CohortManager/src/Functions/CohortDistributionServices/RetrieveCohortDistribution/Program.cs @@ -20,6 +20,7 @@ .ConfigureFunctionsWorkerDefaults() .ConfigureServices(services => { + services.AddTransient(); services.AddTransient(); services.AddSingleton(); services.AddSingleton(); diff --git a/application/CohortManager/src/Functions/CohortDistributionServices/RetrieveCohortDistribution/RetrieveCohortDistribution.cs b/application/CohortManager/src/Functions/CohortDistributionServices/RetrieveCohortDistribution/RetrieveCohortDistribution.cs index 1a32b9030a..6a2bfdd5f5 100644 --- a/application/CohortManager/src/Functions/CohortDistributionServices/RetrieveCohortDistribution/RetrieveCohortDistribution.cs +++ b/application/CohortManager/src/Functions/CohortDistributionServices/RetrieveCohortDistribution/RetrieveCohortDistribution.cs @@ -56,8 +56,8 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymou List cohortDistributionParticipants; try { - int rowCount = _config.MaxRowCount; + bool retrieveSupersededRecordsLast = _config.RetrieveSupersededRecordsLast; if (!string.IsNullOrEmpty(req.Query["rowCount"]) && int.TryParse(req.Query["rowCount"], out int rowCountParam)) { rowCount = Math.Min(rowCount, rowCountParam); @@ -67,7 +67,7 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymou if (string.IsNullOrEmpty(req.Query["requestId"])) { cohortDistributionParticipants = await _createCohortDistributionData - .GetUnextractedCohortDistributionParticipants(rowCount); + .GetUnextractedCohortDistributionParticipants(rowCount, retrieveSupersededRecordsLast); return CreateResponse(cohortDistributionParticipants, req); } @@ -87,7 +87,7 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymou return CreateResponse(cohortDistributionParticipants, req); } - cohortDistributionParticipants = await _createCohortDistributionData.GetUnextractedCohortDistributionParticipants(rowCount); + cohortDistributionParticipants = await _createCohortDistributionData.GetUnextractedCohortDistributionParticipants(rowCount, retrieveSupersededRecordsLast); return CreateResponse(cohortDistributionParticipants, req); diff --git a/application/CohortManager/src/Functions/CohortDistributionServices/RetrieveCohortDistribution/RetrieveCohortDistributionConfig.cs b/application/CohortManager/src/Functions/CohortDistributionServices/RetrieveCohortDistribution/RetrieveCohortDistributionConfig.cs index 76844fcea7..8401357e44 100644 --- a/application/CohortManager/src/Functions/CohortDistributionServices/RetrieveCohortDistribution/RetrieveCohortDistributionConfig.cs +++ b/application/CohortManager/src/Functions/CohortDistributionServices/RetrieveCohortDistribution/RetrieveCohortDistributionConfig.cs @@ -12,5 +12,6 @@ public class RetrieveCohortDistributionConfig [Required] public string BsSelectRequestAuditDataService { get; set; } public int MaxRowCount { get; set; } = 1_000; + public bool RetrieveSupersededRecordsLast { get; set; } = false; } diff --git a/application/CohortManager/src/Functions/Shared/Common/Interfaces/ICreateCohortDistributionData.cs b/application/CohortManager/src/Functions/Shared/Common/Interfaces/ICreateCohortDistributionData.cs index c85392da91..42f08ce3a6 100644 --- a/application/CohortManager/src/Functions/Shared/Common/Interfaces/ICreateCohortDistributionData.cs +++ b/application/CohortManager/src/Functions/Shared/Common/Interfaces/ICreateCohortDistributionData.cs @@ -5,7 +5,7 @@ namespace Common.Interfaces; public interface ICreateCohortDistributionData { - Task> GetUnextractedCohortDistributionParticipants(int rowCount); + Task> GetUnextractedCohortDistributionParticipants(int rowCount, bool retrieveSupersededRecordsLast); Task> GetCohortDistributionParticipantsByRequestId(Guid requestId); Task> GetCohortRequestAudit(string? requestId, string? statusCode, DateTime? dateFrom); Task GetNextCohortRequestAudit(Guid requestId); diff --git a/application/CohortManager/src/Functions/Shared/Common/Interfaces/IExtractCohortDistributionRecordsStrategy.cs b/application/CohortManager/src/Functions/Shared/Common/Interfaces/IExtractCohortDistributionRecordsStrategy.cs new file mode 100644 index 0000000000..c1a4643904 --- /dev/null +++ b/application/CohortManager/src/Functions/Shared/Common/Interfaces/IExtractCohortDistributionRecordsStrategy.cs @@ -0,0 +1,17 @@ +namespace Common.Interfaces; + +using Model; + +/// +/// Strategy interface for extracting cohort distribution participants. +/// +public interface IExtractCohortDistributionRecordsStrategy +{ + /// + /// Gets unextracted cohort distribution participants using the strategy's specific logic. + /// + /// Maximum number of participants to extract + /// Flag to determine if superseded records should be retrieved last + /// List of cohort distribution entities to be extracted + Task> GetUnextractedParticipants(int rowCount, bool retrieveSupersededRecordsLast); +} diff --git a/application/CohortManager/src/Functions/Shared/Data/Database/CreateCohortDistributionData.cs b/application/CohortManager/src/Functions/Shared/Data/Database/CreateCohortDistributionData.cs index ec7258c6a9..7d8a92af9f 100644 --- a/application/CohortManager/src/Functions/Shared/Data/Database/CreateCohortDistributionData.cs +++ b/application/CohortManager/src/Functions/Shared/Data/Database/CreateCohortDistributionData.cs @@ -14,21 +14,35 @@ namespace Data.Database; public class CreateCohortDistributionData : ICreateCohortDistributionData { private readonly IDataServiceClient _cohortDistributionDataServiceClient; - private readonly IDataServiceClient _bsSelectRequestAuditDataServiceClient; + private readonly IExtractCohortDistributionRecordsStrategy? _extractionStrategy; - public CreateCohortDistributionData(IDataServiceClient cohortDistributionDataServiceClient, IDataServiceClient bsSelectRequestAuditDataServiceClient) + public CreateCohortDistributionData( + IDataServiceClient cohortDistributionDataServiceClient, + IDataServiceClient bsSelectRequestAuditDataServiceClient, + IExtractCohortDistributionRecordsStrategy? extractionStrategy = null) { _cohortDistributionDataServiceClient = cohortDistributionDataServiceClient; _bsSelectRequestAuditDataServiceClient = bsSelectRequestAuditDataServiceClient; + _extractionStrategy = extractionStrategy; + } - public async Task> GetUnextractedCohortDistributionParticipants(int rowCount) + public async Task> GetUnextractedCohortDistributionParticipants(int rowCount, bool retrieveSupersededRecordsLast) { - var participantsList = await _cohortDistributionDataServiceClient.GetByFilter(x => x.IsExtracted.Equals(0) && x.RequestId == Guid.Empty); + List participantsToBeExtracted; + // Use new extraction logic if environment variable is set and strategy is injected + if (retrieveSupersededRecordsLast && _extractionStrategy != null) + { + participantsToBeExtracted = await _extractionStrategy.GetUnextractedParticipants(rowCount, retrieveSupersededRecordsLast); + } + else + { + var participantsList = await _cohortDistributionDataServiceClient.GetByFilter(x => x.IsExtracted.Equals(0) && x.RequestId == Guid.Empty); + participantsToBeExtracted = participantsList.OrderBy(x => x.RecordUpdateDateTime ?? x.RecordInsertDateTime).Take(rowCount).ToList(); + } - var participantsToBeExtracted = participantsList.OrderBy(x => x.RecordUpdateDateTime ?? x.RecordInsertDateTime).Take(rowCount).ToList(); //TODO do this filtering on the data services var CohortDistributionParticipantList = participantsToBeExtracted.Select(x => new CohortDistributionParticipant(x)).ToList(); @@ -49,7 +63,6 @@ public async Task> GetUnextractedCohortDi public async Task> GetCohortDistributionParticipantsByRequestId(Guid requestId) { - var requestIdString = requestId.ToString(); if (requestId == Guid.Empty) { CohortDistributionParticipantDto(new List()); @@ -141,16 +154,14 @@ private async Task MarkCohortDistributionParticipantsAsExtracted(List x.CohortDistributionId); - - foreach (var participantId in extractedParticipants) { - var participant = await _cohortDistributionDataServiceClient.GetSingle(participantId.ToString()); participant.IsExtracted = 1; participant.RequestId = requestId; var updatedRecord = await _cohortDistributionDataServiceClient.Update(participant); + if (!updatedRecord) { return false; @@ -205,8 +216,6 @@ private List BuildCohortRequestAudits(IEnumerable x.CreatedDateTime).ToList(); } - - private static Expression> BuildCohortRequestAuditQuery(string? requestId, string? statusCode, DateTime? dateFrom) { var conditions = new List>>(); @@ -231,7 +240,6 @@ private static Expression> BuildCohortRequestAu conditions.Add(predicate); } - Expression> finalPredicate; if (conditions.Count > 0) { @@ -253,7 +261,6 @@ private static Expression> CombineWithAnd(List +/// Extract Cohort Distribution Records without superseded nhs by nhs number first, if none found, get records with superseded by nhs number +/// +public class ExtractCohortDistributionRecords : IExtractCohortDistributionRecordsStrategy +{ + private readonly IDataServiceClient _cohortDistributionDataServiceClient; + + public async Task> GetUnextractedParticipants(int rowCount, bool retrieveSupersededRecordsLast) + { + // Try unextracted participants without superseded by nhs number first, if none found, get records with superseded by nhs number + return await GetRegularUnextractedParticipants(rowCount) + ?? await GetSupersededParticipants(rowCount); + } + + public ExtractCohortDistributionRecords(IDataServiceClient cohortDistributionDataServiceClient) + { + _cohortDistributionDataServiceClient = cohortDistributionDataServiceClient; + } + + private async Task?> GetRegularUnextractedParticipants(int rowCount) + { + var unextractedParticipants = await _cohortDistributionDataServiceClient.GetByFilter( + participant => participant.IsExtracted.Equals(0) && participant.RequestId == Guid.Empty && participant.SupersededNHSNumber == null); + + return unextractedParticipants.Any() + ? OrderAndTakeParticipants(unextractedParticipants, rowCount) + : null; + } + + private async Task> GetSupersededParticipants(int rowCount) + { + var supersededParticipants = await _cohortDistributionDataServiceClient.GetByFilter( + participant => participant.IsExtracted.Equals(0) && participant.RequestId == Guid.Empty && participant.SupersededNHSNumber != null); + + // Get distinct non-null superseded NHS numbers + var supersededNhsNumbers = supersededParticipants + .Select(sp => sp.SupersededNHSNumber.Value) + .Distinct() + .ToList(); + + // Find matching extracted participants + var matchingParticipants = new List(); + foreach (var nhsNumber in supersededNhsNumbers) + { + var matches = await _cohortDistributionDataServiceClient.GetByFilter( + participant => participant.NHSNumber == nhsNumber && participant.IsExtracted.Equals(1)); + matchingParticipants.AddRange(matches); + } + + // Filter superseded participants that have matching records + var filteredParticipants = supersededParticipants + .Where(sp => matchingParticipants.Any(mp => mp.NHSNumber == sp.SupersededNHSNumber)) + .ToList(); + + return OrderAndTakeParticipants(filteredParticipants, rowCount); + } + + private static List OrderAndTakeParticipants(IEnumerable participants, int rowCount) + { + return participants + .OrderBy(participant => (participant.RecordUpdateDateTime ?? participant.RecordInsertDateTime) ?? DateTime.MinValue) + .Take(rowCount) + .ToList(); + } +} diff --git a/infrastructure/tf-core/environments/development.tfvars b/infrastructure/tf-core/environments/development.tfvars index 0e05b544a7..f97f3e8f98 100644 --- a/infrastructure/tf-core/environments/development.tfvars +++ b/infrastructure/tf-core/environments/development.tfvars @@ -671,6 +671,7 @@ function_apps = { ] env_vars_static = { AcceptableLatencyThresholdMs = "500" + RetrieveSupersededRecordsLast = "false" } } diff --git a/infrastructure/tf-core/environments/integration.tfvars b/infrastructure/tf-core/environments/integration.tfvars index bc8ecc46a8..2f273e1a82 100644 --- a/infrastructure/tf-core/environments/integration.tfvars +++ b/infrastructure/tf-core/environments/integration.tfvars @@ -672,6 +672,7 @@ function_apps = { ] env_vars_static = { AcceptableLatencyThresholdMs = "500" + RetrieveSupersededRecordsLast = "false" } } diff --git a/infrastructure/tf-core/environments/nft.tfvars b/infrastructure/tf-core/environments/nft.tfvars index 3ee5a16413..ec3670c612 100644 --- a/infrastructure/tf-core/environments/nft.tfvars +++ b/infrastructure/tf-core/environments/nft.tfvars @@ -671,6 +671,7 @@ function_apps = { ] env_vars_static = { AcceptableLatencyThresholdMs = "500" + RetrieveSupersededRecordsLast = "false" } } diff --git a/infrastructure/tf-core/environments/preprod.tfvars b/infrastructure/tf-core/environments/preprod.tfvars index c867471bd4..a9fa3be860 100644 --- a/infrastructure/tf-core/environments/preprod.tfvars +++ b/infrastructure/tf-core/environments/preprod.tfvars @@ -663,6 +663,7 @@ function_apps = { ] env_vars_static = { AcceptableLatencyThresholdMs = "500" + RetrieveSupersededRecordsLast = "false" } } diff --git a/infrastructure/tf-core/environments/production.tfvars b/infrastructure/tf-core/environments/production.tfvars index 0e721ced5a..398a710261 100644 --- a/infrastructure/tf-core/environments/production.tfvars +++ b/infrastructure/tf-core/environments/production.tfvars @@ -698,6 +698,7 @@ function_apps = { ] env_vars_static = { AcceptableLatencyThresholdMs = "500" + RetrieveSupersededRecordsLast = "false" } } diff --git a/infrastructure/tf-core/environments/sandbox.tfvars b/infrastructure/tf-core/environments/sandbox.tfvars index bb747ad0d3..39ce0b6d72 100644 --- a/infrastructure/tf-core/environments/sandbox.tfvars +++ b/infrastructure/tf-core/environments/sandbox.tfvars @@ -671,6 +671,7 @@ function_apps = { ] env_vars_static = { AcceptableLatencyThresholdMs = "500" + RetrieveSupersededRecordsLast = "false" } } diff --git a/tests/UnitTests/CohortDistributionTests/CohortDistributionDataTests/AddCohortDistributionTests.cs b/tests/UnitTests/CohortDistributionTests/CohortDistributionDataTests/AddCohortDistributionTests.cs index cfeb9adda1..8428332c81 100644 --- a/tests/UnitTests/CohortDistributionTests/CohortDistributionDataTests/AddCohortDistributionTests.cs +++ b/tests/UnitTests/CohortDistributionTests/CohortDistributionDataTests/AddCohortDistributionTests.cs @@ -1,144 +1,370 @@ -namespace NHS.CohortManager.Tests.UnitTests.AddCohortDistributionDataTests; +namespace NHS.CohortManager.Tests.CohortDistributionServiceTests; -using System.Linq.Expressions; +using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; +using Common.Interfaces; using Data.Database; using DataServices.Client; using Model; using Moq; +using System.Linq.Expressions; [TestClass] public class AddCohortDistributionTests { - private readonly CreateCohortDistributionData _createCohortDistributionData; - private readonly Guid _requestId = Guid.NewGuid(); - private List _cohortDistributionList; - private readonly Mock> _cohortDistributionDataServiceClient = new(); - private readonly Mock> _bsSelectRequestAuditDataServiceClient = new(); + private Mock> _cohortDistributionDataServiceClient; + private Mock> _bsSelectRequestAuditDataServiceClient; + private IExtractCohortDistributionRecordsStrategy _extractionStrategy; + private CreateCohortDistributionData _service; + private List _participantList; - public AddCohortDistributionTests() + [TestInitialize] + public void Setup() { - _createCohortDistributionData = new CreateCohortDistributionData(_cohortDistributionDataServiceClient.Object, _bsSelectRequestAuditDataServiceClient.Object); + _cohortDistributionDataServiceClient = new Mock>(); + _bsSelectRequestAuditDataServiceClient = new Mock>(); + _participantList = new List(); + _cohortDistributionDataServiceClient + .Setup(x => x.GetByFilter(It.IsAny>>())) + .ReturnsAsync((Expression> filter) => + _participantList.Where(filter.Compile()).ToList()); + + _cohortDistributionDataServiceClient + .Setup(x => x.GetSingle(It.IsAny())) + .ReturnsAsync((string id) => + _participantList.FirstOrDefault(p => p.CohortDistributionId.ToString() == id)); + + _cohortDistributionDataServiceClient + .Setup(x => x.Update(It.IsAny())) + .ReturnsAsync((CohortDistribution cohort) => + { + var existing = _participantList.FirstOrDefault(c => c.CohortDistributionId == cohort.CohortDistributionId); + if (existing != null) + { + existing.IsExtracted = cohort.IsExtracted; + existing.RequestId = cohort.RequestId; + return true; + } + return false; + }); + + _extractionStrategy = new ExtractCohortDistributionRecords(_cohortDistributionDataServiceClient.Object); + + _service = new CreateCohortDistributionData( + _cohortDistributionDataServiceClient.Object, + _bsSelectRequestAuditDataServiceClient.Object, + _extractionStrategy + ); } + // Tests for old logic when retrieveSupersededRecordsLast is false + [TestMethod] - public async Task ExtractCohortDistributionParticipants_ValidRequest_ReturnsListOfParticipants() + public async Task GetUnextractedCohortDistributionParticipants_ReturnsOneRecordWhenRetrieveSupersededRecordsLastIsFalse() { // Arrange - var listOfValues = new List() + _participantList.AddRange(new List { new CohortDistribution { ParticipantId = 1, - RecordInsertDateTime = DateTime.UtcNow.Date + CohortDistributionId = 1, + NHSNumber = 99900000000, + IsExtracted = 0, + RequestId = Guid.Empty, + SupersededNHSNumber = null, + RecordInsertDateTime = DateTime.UtcNow.AddDays(-1) + }, + new CohortDistribution + { + ParticipantId = 2, + CohortDistributionId = 2, + NHSNumber = 99900000001, + IsExtracted = 0, + RequestId = Guid.Empty, + SupersededNHSNumber = 99988877777, + RecordInsertDateTime = DateTime.UtcNow.AddDays(-1) } - }; + }); - var rowCount = 1; - _cohortDistributionDataServiceClient.Setup(x => x.GetByFilter(It.IsAny>>())).ReturnsAsync(listOfValues); - _cohortDistributionDataServiceClient.Setup(x => x.Update(It.IsAny())).ReturnsAsync(true); - _cohortDistributionDataServiceClient.Setup(x => x.GetSingle(It.IsAny())).ReturnsAsync(new CohortDistribution()); // Act - var result = await _createCohortDistributionData.GetUnextractedCohortDistributionParticipants(rowCount); + var result = await _service.GetUnextractedCohortDistributionParticipants(10, false); // Assert - Assert.AreEqual("1", result.FirstOrDefault()?.ParticipantId); - Assert.AreEqual(1, result.Count); + Assert.IsTrue(result.Count == 2, "Should return two records when retrieveSupersededRecordsLast is false."); } [TestMethod] - public async Task ExtractCohortDistributionParticipants_AfterExtraction_MarksBothParticipantsAsExtracted() + public async Task GetUnextractedCohortDistributionParticipants_ReturnsUpToRowCount() { - // Arrange - _cohortDistributionList = new List + // Arrange - Add 5 participants + for (int i = 1; i <= 5; i++) { - new CohortDistribution + _participantList.Add(new CohortDistribution { - ParticipantId = 1, - IsExtracted = 1 - }, - new CohortDistribution - { - ParticipantId = 2, - IsExtracted = 1 - } - }; - var rowCount = 2; - _cohortDistributionDataServiceClient.Setup(x => x.GetByFilter(It.IsAny>>())).ReturnsAsync(_cohortDistributionList); - _cohortDistributionDataServiceClient.Setup(x => x.Update(It.IsAny())).ReturnsAsync(true); - _cohortDistributionDataServiceClient.Setup(x => x.GetSingle(It.IsAny())).ReturnsAsync(new CohortDistribution()); + ParticipantId = i, + CohortDistributionId = i, + NHSNumber = 9990000000 + i, + IsExtracted = 0, + RequestId = Guid.Empty, + SupersededNHSNumber = null, + RecordInsertDateTime = DateTime.UtcNow.AddMinutes(-i) + }); + } // Act - var result = await _createCohortDistributionData.GetUnextractedCohortDistributionParticipants(rowCount); + var result = await _service.GetUnextractedCohortDistributionParticipants(3, false); // Assert + Assert.AreEqual(3, result.Count()); + } + [TestMethod] + public async Task GetUnextractedCohortDistributionParticipants_ReturnsExpectedDto() + { + // Arrange + _participantList.Add(new CohortDistribution + { + ParticipantId = 1, + CohortDistributionId = 1, + NHSNumber = 99900000000, + IsExtracted = 0, + RequestId = Guid.Empty, + SupersededNHSNumber = 99900000001, + RecordInsertDateTime = DateTime.UtcNow.AddDays(-1) + }); + + // Act - Tests real filtering and DTO mapping logic + var result = await _service.GetUnextractedCohortDistributionParticipants(10, false); + + // Assert + Assert.AreEqual(1, result.Count); + Assert.AreEqual("99900000000", result[0].NhsNumber); + Assert.AreEqual("99900000001", result[0].SupersededByNhsNumber); Assert.AreEqual("1", result[0].ParticipantId); - Assert.AreEqual("1", result[0].IsExtracted); - Assert.AreEqual("2", result[1].ParticipantId); - Assert.AreEqual("1", result[1].IsExtracted); + Assert.AreEqual("0", result[0].IsExtracted); // This has been updated in the DB but DTO reflects original value + Assert.IsTrue(Guid.TryParse(result[0].RequestId, out var guid) && guid != Guid.Empty, "RequestId should be a non-empty GUID."); } [TestMethod] - public async Task GetParticipant_NoParticipants_ReturnsEmptyCollection() + public async Task GetCohortDistributionParticipantsByRequestId_ReturnsEmptyForUnknownRequestId() { // Arrange - var rowCount = 0; + var knownRequestId = Guid.NewGuid(); + var unknownRequestId = Guid.NewGuid(); - // Act - var result = await _createCohortDistributionData.GetUnextractedCohortDistributionParticipants(rowCount); + _participantList.Add(new CohortDistribution + { + ParticipantId = 1, + CohortDistributionId = 1, + NHSNumber = 99900000000, + IsExtracted = 1, + RequestId = knownRequestId, + SupersededNHSNumber = 99900000001, + RecordInsertDateTime = DateTime.UtcNow.AddDays(-1) + }); + + // Act - Tests filtering by RequestId + var result = await _service.GetCohortDistributionParticipantsByRequestId(unknownRequestId); // Assert - Assert.IsNotNull(result); - Assert.AreEqual(0, result.Count); + Assert.AreEqual(0, result.Count, "Should return no participants for an unknown requestId."); } [TestMethod] - public async Task GetCohortDistributionParticipantsByRequestId_RequestId_ReturnsMatchingParticipants() + public async Task GetUnextractedCohortDistributionParticipants_OnlyReturnsSupersededWithMatchingNhsNumber() { + // Arrange + var supersededNhsNumberWithMatch = 99988888888; + + _participantList.Add(new CohortDistribution + { + ParticipantId = 1, + CohortDistributionId = 1, + NHSNumber = 99900000001, + IsExtracted = 0, + RequestId = Guid.Empty, + SupersededNHSNumber = supersededNhsNumberWithMatch, + RecordInsertDateTime = DateTime.UtcNow.AddDays(-1) + }); + // Act - _cohortDistributionDataServiceClient.Setup(x => x.GetByFilter(It.IsAny>>())).ReturnsAsync(new List()); - var validRequestIdResult = await _createCohortDistributionData.GetCohortDistributionParticipantsByRequestId(_requestId); - var inValidRequestIdResult = await _createCohortDistributionData.GetCohortDistributionParticipantsByRequestId(Guid.Empty); + var result = await _service.GetUnextractedCohortDistributionParticipants(10, false); // Assert - Assert.AreEqual(0, inValidRequestIdResult.Count); + Assert.AreEqual(1, result.Count, "Only one superseded participant should be extracted (the one with a matching NHSNumber)."); + Assert.AreEqual("99900000001", result[0].NhsNumber); + Assert.AreEqual(supersededNhsNumberWithMatch.ToString(), result[0].SupersededByNhsNumber); } +// Tests for new logic when retrieveSupersededRecordsLast is true [TestMethod] - public async Task GetCohortDistributionParticipantsByRequestId_NoParticipants_ReturnsEmptyList() + public async Task GetUnextractedCohortDistributionParticipants_ReturnsEmptyWhenNoSupersededMatch() { // Arrange + _participantList.Add(new CohortDistribution + { + ParticipantId = 1, + CohortDistributionId = 1, + NHSNumber = 99900000001, + IsExtracted = 0, + RequestId = Guid.Empty, + SupersededNHSNumber = 99988888888, // No matching extracted record with this NHS number + RecordInsertDateTime = DateTime.UtcNow.AddDays(-1) + }); // Act - var result = await _createCohortDistributionData.GetCohortDistributionParticipantsByRequestId(_requestId); + var result = await _service.GetUnextractedCohortDistributionParticipants(10, true); // Assert - Assert.IsNotNull(result); - Assert.AreEqual(0, result.Count); + Assert.AreEqual(0, result.Count, "Should return no participants when there is no matching extracted NHSNumber."); + } + + [TestMethod] + public async Task GetUnextractedCohortDistributionParticipants_UsesExtractionStrategyWhenRetrieveSupersededRecordsLastIsTrue() + { + // Arrange + _participantList.Add(new CohortDistribution + { + ParticipantId = 1, + CohortDistributionId = 1, + NHSNumber = 99900000000, + IsExtracted = 0, + RequestId = Guid.Empty, + SupersededNHSNumber = null, + RecordInsertDateTime = DateTime.UtcNow.AddDays(-1) + }); + + // Act - This now tests the real ExtractCohortDistributionRecords strategy + var result = await _service.GetUnextractedCohortDistributionParticipants(10, true); + + // Assert - Verify the strategy correctly extracted the participant + Assert.AreEqual(1, result.Count); + Assert.AreEqual("99900000000", result[0].NhsNumber); } + [TestMethod] - public async Task GetCohortDistributionParticipantsByRequestId_ValidRequestId_ReturnsParticipants() + public async Task GetUnextractedCohortDistributionParticipants_ReturnsAllWhenAllSupersededMatch() { - var requestId = new Guid(); - var listOfValues = new List() + // Arrange + var supersededNhsNumbers = new[] { 99911111111, 99922222222}; + + // Add the extracted records that the superseded records will match against + _participantList.AddRange(new List + { + new CohortDistribution + { + ParticipantId = 10, + CohortDistributionId = 10, + NHSNumber = supersededNhsNumbers[0], + IsExtracted = 1, + RequestId = Guid.NewGuid(), + SupersededNHSNumber = null, + RecordInsertDateTime = DateTime.UtcNow.AddDays(-10) + }, + new CohortDistribution + { + ParticipantId = 11, + CohortDistributionId = 11, + NHSNumber = supersededNhsNumbers[1], + IsExtracted = 1, + RequestId = Guid.NewGuid(), + SupersededNHSNumber = null, + RecordInsertDateTime = DateTime.UtcNow.AddDays(-11) + } + }); + + // Add the unextracted superseded participants + _participantList.AddRange(new List { new CohortDistribution { ParticipantId = 1, - RecordInsertDateTime = DateTime.UtcNow.Date, - RequestId = requestId + CohortDistributionId = 1, + NHSNumber = 99900000001, + IsExtracted = 0, + RequestId = Guid.Empty, + SupersededNHSNumber = supersededNhsNumbers[0], + RecordInsertDateTime = DateTime.UtcNow.AddDays(-1) + }, + new CohortDistribution + { + ParticipantId = 2, + CohortDistributionId = 2, + NHSNumber = 99900000002, + IsExtracted = 0, + RequestId = Guid.Empty, + SupersededNHSNumber = supersededNhsNumbers[1], + RecordInsertDateTime = DateTime.UtcNow.AddDays(-2) } - }; + }); - _cohortDistributionDataServiceClient.Setup(x => x.GetByFilter(It.IsAny>>())).ReturnsAsync(listOfValues); // Act - var result = await _createCohortDistributionData.GetCohortDistributionParticipantsByRequestId(_requestId); + var result = await _service.GetUnextractedCohortDistributionParticipants(10, true); // Assert - Assert.IsNotNull(result); - Assert.AreEqual(1, result.Count); - Assert.AreEqual("1", result[0].ParticipantId); - Assert.AreEqual(requestId.ToString(), result[0].RequestId); + Assert.AreEqual(2, result.Count, "Should return all superseded participants when all have a matching extracted NHSNumber."); + } + + [TestMethod] + public async Task GetCohortDistributionParticipantsByRequestId_ReturnsExpectedDto() + { + // Arrange + var requestId = Guid.NewGuid(); + var otherRequestId = Guid.NewGuid(); + + _participantList.AddRange(new List + { + new CohortDistribution + { + ParticipantId = 1, + CohortDistributionId = 1, + NHSNumber = 99900000000, + IsExtracted = 1, + RequestId = requestId, + SupersededNHSNumber = 99900000001, + RecordInsertDateTime = DateTime.UtcNow + }, + new CohortDistribution + { + ParticipantId = 2, + CohortDistributionId = 2, + NHSNumber = 99900000002, + IsExtracted = 1, + RequestId = otherRequestId, + SupersededNHSNumber = null, + RecordInsertDateTime = DateTime.UtcNow + }, + new CohortDistribution + { + ParticipantId = 3, + CohortDistributionId = 3, + NHSNumber = 99900000003, + IsExtracted = 1, + RequestId = requestId, + SupersededNHSNumber = null, + RecordInsertDateTime = DateTime.UtcNow + } + }); + + // Act + var result = await _service.GetCohortDistributionParticipantsByRequestId(requestId); + + // Assert correct participants are returned. This should return all records with the requestId, regardless of superseded by nhs number status + Assert.AreEqual(2, result.Count, "Should return only participants with matching RequestId"); + Assert.IsTrue(result.All(r => r.RequestId == requestId.ToString()), "All returned DTOs should have the correct RequestId."); + + // Assert returned DTOs match expected data + var expected = _participantList.Where(c => c.RequestId == requestId).ToList(); + for (int i = 0; i < 2; i++) + { + Assert.AreEqual(expected[i].NHSNumber.ToString(), result[i].NhsNumber); + Assert.AreEqual(expected[i].SupersededNHSNumber?.ToString() ?? "", result[i].SupersededByNhsNumber); + Assert.AreEqual(expected[i].ParticipantId.ToString(), result[i].ParticipantId); + Assert.AreEqual(expected[i].IsExtracted.ToString(), result[i].IsExtracted); + Assert.AreEqual(expected[i].RequestId.ToString(), result[i].RequestId); + } } } diff --git a/tests/UnitTests/CohortDistributionTests/CohortDistributionDataTests/CohortDistributionDataTests.csproj b/tests/UnitTests/CohortDistributionTests/CohortDistributionDataTests/CohortDistributionDataTests.csproj index 9ac39a1f55..3a7866fecb 100644 --- a/tests/UnitTests/CohortDistributionTests/CohortDistributionDataTests/CohortDistributionDataTests.csproj +++ b/tests/UnitTests/CohortDistributionTests/CohortDistributionDataTests/CohortDistributionDataTests.csproj @@ -23,7 +23,8 @@ - + +