Skip to content

Commit 99a1712

Browse files
authored
* Fix error where the ChangeProcessor.GetKeyChangeDependencies method would throw an exception when passed exactly one post-dependency. (Ed-Fi-Alliance-OSS#106)
1 parent 9ada2fa commit 99a1712

2 files changed

Lines changed: 209 additions & 9 deletions

File tree

src/EdFi.Tools.ApiPublisher.Core/Processing/ChangeProcessor.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
44
// See the LICENSE and NOTICES files in the project root for more information.
55

6+
using System;
7+
using System.Collections.Generic;
8+
using System.Diagnostics;
9+
using System.Linq;
10+
using System.Text;
11+
using System.Threading;
12+
using System.Threading.Tasks;
13+
using System.Threading.Tasks.Dataflow;
614
using Autofac.Features.Indexed;
715
using EdFi.Tools.ApiPublisher.Core.Capabilities;
816
using EdFi.Tools.ApiPublisher.Core.Configuration;
@@ -18,14 +26,6 @@
1826
using Polly.RateLimit;
1927
using Serilog;
2028
using Serilog.Events;
21-
using System;
22-
using System.Collections.Generic;
23-
using System.Diagnostics;
24-
using System.Linq;
25-
using System.Text;
26-
using System.Threading;
27-
using System.Threading.Tasks;
28-
using System.Threading.Tasks.Dataflow;
2929

3030
namespace EdFi.Tools.ApiPublisher.Core.Processing
3131
{
@@ -898,7 +898,7 @@ private IDictionary<string, string[]> GetKeyChangeDependencies(IDictionary<strin
898898
i++;
899899
}
900900

901-
if (i == infiniteLoopProtectionThreshold)
901+
if (i == infiniteLoopProtectionThreshold && i != 1)
902902
{
903903
// This should never happen
904904
throw new Exception(
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Licensed to the Ed-Fi Alliance under one or more agreements.
3+
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
4+
// See the LICENSE and NOTICES files in the project root for more information.
5+
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Reflection;
9+
using EdFi.Tools.ApiPublisher.Core.Processing;
10+
using EdFi.Tools.ApiPublisher.Tests.Helpers;
11+
using Microsoft.Extensions.Configuration;
12+
using NUnit.Framework;
13+
14+
namespace EdFi.Tools.ApiPublisher.Tests.Processing
15+
{
16+
[TestFixture]
17+
public class ChangeProcessorTests
18+
{
19+
20+
private ChangeProcessor GetChangeProcessor()
21+
{
22+
// -----------------------------------------------------------------
23+
// Source Requests
24+
// -----------------------------------------------------------------
25+
var sourceResourceFaker = TestHelpers.GetGenericResourceFaker();
26+
27+
var suppliedSourceResources = sourceResourceFaker.Generate(5);
28+
29+
// Prepare the fake source API endpoint
30+
var _fakeSourceRequestHandler = TestHelpers.GetFakeBaselineSourceApiRequestHandler()
31+
32+
// Test-specific mocks
33+
.AvailableChangeVersions(1100)
34+
.ResourceCount(responseTotalCountHeader: 1)
35+
.GetResourceData($"{EdFiApiConstants.DataManagementApiSegment}{TestHelpers.AnyResourcePattern}", suppliedSourceResources)
36+
.GetResourceData($"{EdFiApiConstants.DataManagementApiSegment}{TestHelpers.AnyResourcePattern}/deletes", Array.Empty<object>());
37+
38+
// -----------------------------------------------------------------
39+
40+
// -----------------------------------------------------------------
41+
// Target Requests
42+
// -----------------------------------------------------------------
43+
44+
var _fakeTargetRequestHandler = TestHelpers.GetFakeBaselineTargetApiRequestHandler();
45+
46+
// Every POST succeeds
47+
_fakeTargetRequestHandler.EveryDataManagementPostReturns200Ok();
48+
49+
// -----------------------------------------------------------------
50+
// Source/Target Connection Details
51+
// -----------------------------------------------------------------
52+
53+
var sourceApiConnectionDetails = TestHelpers.GetSourceApiConnectionDetails(
54+
exclude: new[] { "assessments", "/ed-fi/sections", "/tpdm/candidates" });
55+
56+
var targetApiConnectionDetails = TestHelpers.GetTargetApiConnectionDetails();
57+
58+
// -----------------------------------------------------------------
59+
// Options and Configuration
60+
// -----------------------------------------------------------------
61+
62+
var options = TestHelpers.GetOptions();
63+
options.IncludeDescriptors = false; // Shorten test execution time
64+
// -----------------------------------------------------------------
65+
66+
// Initialize logging
67+
TestHelpers.InitializeLogging();
68+
var configurationStoreSection = null as IConfigurationSection;
69+
70+
// Configuration
71+
var _changeProcessorConfiguration = TestHelpers.CreateChangeProcessorConfiguration(options);
72+
73+
// Create change processor with dependencies
74+
var _changeProcessor = TestHelpers.CreateChangeProcessorWithDefaultDependencies(
75+
options,
76+
sourceApiConnectionDetails,
77+
_fakeSourceRequestHandler,
78+
targetApiConnectionDetails,
79+
_fakeTargetRequestHandler);
80+
81+
return _changeProcessor;
82+
83+
}
84+
85+
[Test]
86+
public void GetKeyChangeDependenciesTest_PostDependencies_SingleResourceWithNoDependencies_DoesNotThrow()
87+
{
88+
// Arrange
89+
var changeProcessor = GetChangeProcessor();
90+
91+
var method = typeof(ChangeProcessor).GetMethod("GetKeyChangeDependencies", BindingFlags.NonPublic | BindingFlags.Instance);
92+
93+
var postDependencies = new Dictionary<string, string[]>
94+
{
95+
{ "/ed-fi/assessment", Array.Empty<string>() },
96+
};
97+
98+
var updatableKeys = new[] {
99+
"/ed-fi/classPeriods",
100+
"/ed-fi/grades",
101+
"/ed-fi/gradebookEntries",
102+
"/ed-fi/locations",
103+
"/ed-fi/sections",
104+
"/ed-fi/sessions",
105+
"/ed-fi/studentSchoolAssociations",
106+
"/ed-fi/studentSectionAssociations"
107+
};
108+
109+
// Assert
110+
Assert.DoesNotThrow(() =>
111+
{
112+
// Act
113+
var result = (IDictionary<string, string[]>)method.Invoke(changeProcessor, new object[] { postDependencies, updatableKeys });
114+
});
115+
116+
}
117+
118+
119+
[Test]
120+
public void GetKeyChangeDependenciesTest_PostDependencies_SingleResourceWithNestedDependencies_DoesNotThrow()
121+
{
122+
// Arrange
123+
var changeProcessor = GetChangeProcessor();
124+
125+
var method = typeof(ChangeProcessor).GetMethod("GetKeyChangeDependencies", BindingFlags.NonPublic | BindingFlags.Instance);
126+
127+
var postDependencies = new Dictionary<string, string[]>
128+
{
129+
["/ed-fi/accountabilityRatings"] = new[] { "/ed-fi/communityOrganizations", "/ed-fi/communityProviders", "/ed-fi/educationOrganizationNetworks", "/ed-fi/educationServiceCenters" },
130+
};
131+
132+
var updatableKeys = new[] {
133+
"/ed-fi/classPeriods",
134+
"/ed-fi/grades",
135+
"/ed-fi/gradebookEntries",
136+
"/ed-fi/locations",
137+
"/ed-fi/sections",
138+
"/ed-fi/sessions",
139+
"/ed-fi/studentSchoolAssociations",
140+
"/ed-fi/studentSectionAssociations"
141+
};
142+
143+
144+
// Assert
145+
Assert.DoesNotThrow(() =>
146+
{
147+
// Act
148+
var result = (IDictionary<string, string[]>)method.Invoke(changeProcessor, new object[] { postDependencies, updatableKeys });
149+
});
150+
151+
}
152+
153+
154+
[Test]
155+
public void GetKeyChangeDependenciesTest_PostDependencies_MultipleResourcesWithComplexDependencies_DoesNotThrow()
156+
{
157+
// Arrange
158+
var changeProcessor = GetChangeProcessor();
159+
160+
var method = typeof(ChangeProcessor).GetMethod("GetKeyChangeDependencies", BindingFlags.NonPublic | BindingFlags.Instance);
161+
162+
var postDependencies = new Dictionary<string, string[]>
163+
{
164+
["/ed-fi/academicWeeks"] = new[] { "/ed-fi/schools" },
165+
["/ed-fi/accountabilityRatings"] = new[] { "/ed-fi/communityOrganizations", "/ed-fi/communityProviders", "/ed-fi/educationOrganizationNetworks", "/ed-fi/educationServiceCenters" },
166+
["/ed-fi/assessmentItems"] = new[] { "/ed-fi/assessments", "/ed-fi/learningStandards" },
167+
["/ed-fi/assessments"] = new[] { "/ed-fi/communityOrganizations", "/ed-fi/communityProviders", "/ed-fi/educationOrganizationNetworks", "/ed-fi/educationServiceCenters" },
168+
["/ed-fi/assessmentScoreRangeLearningStandards"] = new[] { "/ed-fi/assessments", "/ed-fi/learningStandards", "/ed-fi/objectiveAssessments" },
169+
["/ed-fi/balanceSheetDimensions"] = Array.Empty<string>(),
170+
["/ed-fi/bellSchedules"] = new[] { "/ed-fi/classPeriods", "/ed-fi/schools" },
171+
["/ed-fi/calendarDates"] = new[] { "/ed-fi/calendars" },
172+
["/ed-fi/calendars"] = new[] { "/ed-fi/schools" },
173+
["/ed-fi/chartOfAccounts"] = new[] { "/ed-fi/balanceSheetDimensions", "/ed-fi/communityOrganizations", "/ed-fi/communityProviders", "/ed-fi/educationOrganizationNetworks" },
174+
["/ed-fi/classPeriods"] = new[] { "/ed-fi/schools" },
175+
["/ed-fi/cohorts"] = new[] { "/ed-fi/communityOrganizations", "/ed-fi/communityProviders", "/ed-fi/educationOrganizationNetworks", "/ed-fi/educationServiceCenters" },
176+
["/ed-fi/communityOrganizations"] = Array.Empty<string>()
177+
};
178+
179+
var updatableKeys = new[] {
180+
"/ed-fi/classPeriods",
181+
"/ed-fi/grades",
182+
"/ed-fi/gradebookEntries",
183+
"/ed-fi/locations",
184+
"/ed-fi/sections",
185+
"/ed-fi/sessions",
186+
"/ed-fi/studentSchoolAssociations",
187+
"/ed-fi/studentSectionAssociations"
188+
};
189+
190+
191+
// Assert
192+
Assert.DoesNotThrow(() =>
193+
{
194+
// Act
195+
var result = (IDictionary<string, string[]>)method.Invoke(changeProcessor, new object[] { postDependencies, updatableKeys });
196+
});
197+
198+
}
199+
}
200+
}

0 commit comments

Comments
 (0)