From c778f0f8a91d833157e85e11df591a86d86a83c3 Mon Sep 17 00:00:00 2001 From: Andrew Horth Date: Mon, 16 Dec 2024 13:52:06 +0000 Subject: [PATCH] Added fixes while testing against CRM build environment data --- .../Jobs/SyncAllInductionsFromCrmJob.cs | 11 +++- .../Services/TrsDataSync/TrsDataSyncHelper.cs | 23 ++++++- .../Jobs/SyncAllInductionsFromCrmJobTests.cs | 59 +++++++++++++++++ .../Jobs/SyncFromCrmJobFixture.cs | 63 +++++++++++++++++++ .../Jobs/SyncFromCrmJobTestBase.cs | 24 +++++++ .../TrsDataSyncHelperTests.Induction.cs | 26 ++++++++ 6 files changed, 202 insertions(+), 4 deletions(-) create mode 100644 TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Jobs/SyncAllInductionsFromCrmJobTests.cs create mode 100644 TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Jobs/SyncFromCrmJobFixture.cs create mode 100644 TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Jobs/SyncFromCrmJobTestBase.cs diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Jobs/SyncAllInductionsFromCrmJob.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Jobs/SyncAllInductionsFromCrmJob.cs index 47edfbcde..0a30d8cb8 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Jobs/SyncAllInductionsFromCrmJob.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Jobs/SyncAllInductionsFromCrmJob.cs @@ -31,6 +31,8 @@ public async Task ExecuteAsync(bool createMigratedEvent, bool dryRun, Cancellati var serviceClient = _crmServiceClientProvider.GetClient(TrsDataSyncService.CrmClientName); var columns = new ColumnSet(TrsDataSyncHelper.GetEntityInfoForModelType(TrsDataSyncHelper.ModelTypes.Induction).AttributeNames); + var filter = new FilterExpression(); + filter.AddCondition(Contact.Fields.dfeta_InductionStatus, ConditionOperator.NotNull); var query = new QueryExpression(Contact.EntityLogicalName) { @@ -43,7 +45,9 @@ public async Task ExecuteAsync(bool createMigratedEvent, bool dryRun, Cancellati { Count = pageSize, PageNumber = 1 - } + }, + Criteria = filter + }; while (true) @@ -70,6 +74,11 @@ await _trsDataSyncHelper.SyncInductionsAsync( if (result.MoreRecords) { + if (query.PageInfo.PageNumber == 100) + { + break; + } + query.PageInfo.PageNumber++; query.PageInfo.PagingCookie = result.PagingCookie; } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/TrsDataSync/TrsDataSyncHelper.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/TrsDataSync/TrsDataSyncHelper.cs index a4d266b57..e0a81682a 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/TrsDataSync/TrsDataSyncHelper.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/TrsDataSync/TrsDataSyncHelper.cs @@ -330,7 +330,8 @@ public async Task SyncInductionsAsync( dfeta_induction.Fields.dfeta_InductionStatus, dfeta_induction.Fields.CreatedOn, dfeta_induction.Fields.CreatedBy, - dfeta_induction.Fields.ModifiedOn + dfeta_induction.Fields.ModifiedOn, + dfeta_induction.Fields.StateCode }; var inductions = await GetEntitiesAsync( @@ -644,6 +645,7 @@ private EntityVersionInfo[] GetEntityVersions(TEntity latest, .OfType() .Select(a => (AuditDetail: a, AuditRecord: a.AuditRecord.ToEntity())) .OrderBy(a => a.AuditRecord.CreatedOn) + .ThenBy(a => a.AuditRecord.Action == Audit_Action.Create ? 0 : 1) .ToArray(); if (ordered.Length == 0) @@ -1012,7 +1014,21 @@ RETURNING t.person_id { Contact.PrimaryIdAttribute, Contact.Fields.dfeta_InductionStatus, - Contact.Fields.dfeta_qtlsdate + Contact.Fields.dfeta_qtlsdate, + Contact.Fields.CreatedOn, + Contact.Fields.CreatedBy, + Contact.Fields.StateCode, + Contact.Fields.ModifiedOn, + Contact.Fields.dfeta_TRN, + Contact.Fields.FirstName, + Contact.Fields.MiddleName, + Contact.Fields.LastName, + Contact.Fields.dfeta_StatedFirstName, + Contact.Fields.dfeta_StatedMiddleName, + Contact.Fields.dfeta_StatedLastName, + Contact.Fields.BirthDate, + Contact.Fields.dfeta_NINumber, + Contact.Fields.EMailAddress1, }; Action writeRecord = (writer, induction) => @@ -1225,7 +1241,8 @@ private static List MapPersons(IEnumerable contacts) => contact dfeta_induction.Fields.dfeta_InductionExemptionReason, dfeta_induction.Fields.dfeta_StartDate, dfeta_induction.Fields.dfeta_InductionStatus, - dfeta_induction.Fields.ModifiedOn + dfeta_induction.Fields.ModifiedOn, + dfeta_induction.Fields.StateCode }; if (auditDetails.TryGetValue(induction!.Id, out var inductionAudits)) diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Jobs/SyncAllInductionsFromCrmJobTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Jobs/SyncAllInductionsFromCrmJobTests.cs new file mode 100644 index 000000000..181e7ea5d --- /dev/null +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Jobs/SyncAllInductionsFromCrmJobTests.cs @@ -0,0 +1,59 @@ +using Microsoft.Extensions.Options; +using TeachingRecordSystem.Core.Dqt.Models; +using TeachingRecordSystem.Core.Jobs; +using TeachingRecordSystem.Core.Services.TrsDataSync; +using TeachingRecordSystem.Core.Tests.Services.TrsDataSync; + +namespace TeachingRecordSystem.Core.Tests.Jobs; + +[CollectionDefinition(nameof(TrsDataSyncTestCollection), DisableParallelization = true)] +public class SyncAllInductionsFromCrmJobTests : SyncFromCrmJobTestBase, IAsyncLifetime +{ + public SyncAllInductionsFromCrmJobTests(SyncFromCrmJobFixture jobFixture) : base(jobFixture) + { + } + + [Fact] + public async Task SyncInductionsAsync_WithExistingDqtInduction_UpdatesPersonRecord() + { + // Arrange + var inductionStatus = dfeta_InductionStatus.Pass; + var inductionStartDate = Clock.Today.AddYears(-1); + var inductionCompletedDate = Clock.Today.AddDays(-5); + var options = Options.Create(new TrsDataSyncServiceOptions() + { + CrmConnectionString = "dummy", + ModelTypes = [TrsDataSyncHelper.ModelTypes.Person], + PollIntervalSeconds = 60, + IgnoreInvalidData = false, + RunService = false + }); + + var person = await TestData.CreatePersonAsync( + p => p.WithTrn() + .WithQts() + .WithDqtInduction(inductionStatus, null, inductionStartDate, inductionCompletedDate) + .WithSyncOverride(false)); + + // Act + var job = new SyncAllInductionsFromCrmJob( + CrmServiceClientProvider, + Helper, + options); + + await job.ExecuteAsync(createMigratedEvent: false, dryRun: false, CancellationToken.None); + + // Assert + await DbFixture.WithDbContextAsync(async dbContext => + { + var updatedPerson = await dbContext.Persons.SingleOrDefaultAsync(p => p.DqtContactId == person.ContactId); + Assert.Equal(inductionStatus.ToInductionStatus(), updatedPerson!.InductionStatus); + Assert.Equal(inductionStartDate, updatedPerson.InductionStartDate); + Assert.Equal(inductionCompletedDate, updatedPerson.InductionCompletedDate); + }); + } + + Task IAsyncLifetime.DisposeAsync() => Task.CompletedTask; + + Task IAsyncLifetime.InitializeAsync() => JobFixture.DbFixture.DbHelper.ClearDataAsync(); +} diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Jobs/SyncFromCrmJobFixture.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Jobs/SyncFromCrmJobFixture.cs new file mode 100644 index 000000000..0676a83b4 --- /dev/null +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Jobs/SyncFromCrmJobFixture.cs @@ -0,0 +1,63 @@ +using Microsoft.PowerPlatform.Dataverse.Client; +using TeachingRecordSystem.Core.Dqt; +using TeachingRecordSystem.Core.Services.TrsDataSync; + +namespace TeachingRecordSystem.Core.Tests.Jobs; + +public class SyncFromCrmJobFixture : IAsyncLifetime +{ + public SyncFromCrmJobFixture( + DbFixture dbFixture, + IOrganizationServiceAsync2 organizationService, + ReferenceDataCache referenceDataCache, + FakeTrnGenerator trnGenerator) + { + DbFixture = dbFixture; + Clock = new(); + + Helper = new TrsDataSyncHelper( + dbFixture.GetDataSource(), + organizationService, + referenceDataCache, + Clock); + + TestData = new TestData( + dbFixture.GetDbContextFactory(), + organizationService, + referenceDataCache, + Clock, + trnGenerator, + TestDataSyncConfiguration.Sync(Helper)); + + CrmServiceClientProvider = new TestCrmServiceClientProvider(organizationService); + } + + public TestableClock Clock { get; } + + public DbFixture DbFixture { get; } + + public TrsDataSyncHelper Helper { get; } + + public TestData TestData { get; } + + public ICrmServiceClientProvider CrmServiceClientProvider { get; } + + Task IAsyncLifetime.DisposeAsync() => Task.CompletedTask; + + Task IAsyncLifetime.InitializeAsync() => DbFixture.DbHelper.ClearDataAsync(); + + private class TestCrmServiceClientProvider : ICrmServiceClientProvider + { + private readonly IOrganizationServiceAsync2 _organizationService; + + public TestCrmServiceClientProvider(IOrganizationServiceAsync2 organizationService) + { + _organizationService = organizationService; + } + + public IOrganizationServiceAsync2 GetClient(string name) + { + return _organizationService; + } + } +} diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Jobs/SyncFromCrmJobTestBase.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Jobs/SyncFromCrmJobTestBase.cs new file mode 100644 index 000000000..f8b259229 --- /dev/null +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Jobs/SyncFromCrmJobTestBase.cs @@ -0,0 +1,24 @@ +using TeachingRecordSystem.Core.Dqt; +using TeachingRecordSystem.Core.Services.TrsDataSync; + +namespace TeachingRecordSystem.Core.Tests.Jobs; + +public abstract class SyncFromCrmJobTestBase : IClassFixture +{ + public SyncFromCrmJobTestBase(SyncFromCrmJobFixture jobFixture) + { + JobFixture = jobFixture; + } + + public SyncFromCrmJobFixture JobFixture { get; } + + protected TestableClock Clock => JobFixture.Clock; + + protected DbFixture DbFixture => JobFixture.DbFixture; + + protected TrsDataSyncHelper Helper => JobFixture.Helper; + + protected TestData TestData => JobFixture.TestData; + + public ICrmServiceClientProvider CrmServiceClientProvider => JobFixture.CrmServiceClientProvider; +} diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Services/TrsDataSync/TrsDataSyncHelperTests.Induction.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Services/TrsDataSync/TrsDataSyncHelperTests.Induction.cs index 1a86e3842..719541b5b 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Services/TrsDataSync/TrsDataSyncHelperTests.Induction.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Services/TrsDataSync/TrsDataSyncHelperTests.Induction.cs @@ -96,6 +96,32 @@ await DbFixture.WithDbContextAsync(async dbContext => }); } + [Fact] + public async Task SyncInductionsAsync_WithExistingDqtInduction_UpdatesPersonRecord() + { + // Arrange + var inductionStatus = dfeta_InductionStatus.InProgress; + var inductionStartDate = Clock.Today.AddYears(-1); + + var person = await TestData.CreatePersonAsync( + p => p.WithTrn() + .WithQts() + .WithDqtInduction(inductionStatus, null, inductionStartDate, null) + .WithSyncOverride(false)); + + // Act + await Helper.SyncInductionsAsync([person.Contact], ignoreInvalid: true, createMigratedEvent: false, dryRun: false, CancellationToken.None); + + // Assert + await DbFixture.WithDbContextAsync(async dbContext => + { + var updatedPerson = await dbContext.Persons.SingleOrDefaultAsync(p => p.DqtContactId == person.ContactId); + Assert.Equal(inductionStatus.ToInductionStatus(), updatedPerson!.InductionStatus); + Assert.Equal(inductionStartDate, updatedPerson.InductionStartDate); + + }); + } + [Fact] public async Task SyncInductionsAsync_WithQtlsButNotExemptAndIgnoreInvalidSetToFalse_ThrowsException() {