Skip to content

Commit 8a3e18b

Browse files
authored
Fix nesting in entity registry service, add unit tests (#35)
1 parent ecf5d4e commit 8a3e18b

File tree

5 files changed

+273
-17
lines changed

5 files changed

+273
-17
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
8+
<IsPackable>false</IsPackable>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
13+
<PackageReference Include="Moq" Version="4.18.2" />
14+
<PackageReference Include="MSTest.TestAdapter" Version="2.2.8" />
15+
<PackageReference Include="MSTest.TestFramework" Version="2.2.8" />
16+
<PackageReference Include="coverlet.collector" Version="3.1.2" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<ProjectReference Include="..\Dan.Common\Dan.Common.csproj" />
21+
</ItemGroup>
22+
23+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
namespace Dan.Common.UnitTest.Services;
2+
3+
[TestClass]
4+
[ExcludeFromCodeCoverage]
5+
public class EntityRegistryServiceTest
6+
{
7+
private readonly Mock<IEntityRegistryApiClientService> _entityRegistryApiClientServiceMock = new();
8+
private readonly IEntityRegistryService _entityRegistryService;
9+
10+
public EntityRegistryServiceTest()
11+
{
12+
_entityRegistryService = new EntityRegistryService(_entityRegistryApiClientServiceMock.Object);
13+
}
14+
15+
[TestInitialize]
16+
public void TestInitialize()
17+
{
18+
_entityRegistryApiClientServiceMock.Setup(_ => _.GetUpstreamEntityRegistryUnitAsync(It.IsAny<Uri>()))
19+
.Returns((Uri url) =>
20+
{
21+
var orgForm = new Organisasjonsform { Kode = "AS" };
22+
if (url.AbsolutePath.Contains("/enheter/91"))
23+
{
24+
return Task.FromResult(new EntityRegistryUnit { Organisasjonsnummer = "91", Organisasjonsform = orgForm })!;
25+
}
26+
27+
if (url.AbsolutePath.Contains("/underenheter/92"))
28+
{
29+
return Task.FromResult(new EntityRegistryUnit { Organisasjonsnummer = "92", OverordnetEnhet = "91", Organisasjonsform = orgForm })!;
30+
}
31+
32+
// Only subunits are at the leaf node, any nested parents are MainUnits
33+
// Example: https://data.brreg.no/enhetsregisteret/api/underenheter/879587662
34+
if (url.AbsolutePath.Contains("/enheter/93"))
35+
{
36+
return Task.FromResult(new EntityRegistryUnit { Organisasjonsnummer = "93", OverordnetEnhet = "91", Organisasjonsform = orgForm })!;
37+
}
38+
39+
if (url.AbsolutePath.Contains("/underenheter/94"))
40+
{
41+
return Task.FromResult(new EntityRegistryUnit { Organisasjonsnummer = "94", OverordnetEnhet = "93", Organisasjonsform = orgForm })!;
42+
}
43+
44+
if (url.AbsolutePath.Contains("/underenheter/31"))
45+
{
46+
return Task.FromResult(new EntityRegistryUnit { Organisasjonsnummer = "31", Organisasjonsform = orgForm })!;
47+
}
48+
49+
return Task.FromResult<EntityRegistryUnit?>(null);
50+
});
51+
}
52+
53+
[TestMethod]
54+
public void TestGetMapping()
55+
{
56+
var s = new EntityRegistryService(_entityRegistryApiClientServiceMock.Object) { UseCoreProxy = false };
57+
var r = s.Get("91").Result;
58+
Assert.AreEqual("91", r?.OrganizationNumber);
59+
}
60+
61+
[TestMethod]
62+
public void TestGetAttemptSubUnitLookupIfNotFoundReturnsSubUnit()
63+
{
64+
var s = new EntityRegistryService(_entityRegistryApiClientServiceMock.Object) { UseCoreProxy = false };
65+
var r = s.Get("92").Result;
66+
Assert.AreEqual("92", r?.OrganizationNumber);
67+
}
68+
69+
[TestMethod]
70+
public void TestGetAttemptSubUnitLookupIfNotFoundSetToFalseReturnsNull()
71+
{
72+
var s = new EntityRegistryService(_entityRegistryApiClientServiceMock.Object) { UseCoreProxy = false };
73+
var r = s.Get("92", attemptSubUnitLookupIfNotFound: false).Result;
74+
Assert.IsNull(r);
75+
}
76+
77+
[TestMethod]
78+
public void TestGetMainUnit()
79+
{
80+
var s = new EntityRegistryService(_entityRegistryApiClientServiceMock.Object) { UseCoreProxy = false };
81+
var r = s.GetMainUnit("91").Result;
82+
Assert.AreEqual("91", r?.OrganizationNumber);
83+
}
84+
85+
[TestMethod]
86+
public void TestGetMainUnitAttemptsSubunitLookup()
87+
{
88+
var s = new EntityRegistryService(_entityRegistryApiClientServiceMock.Object) { UseCoreProxy = false };
89+
var r = s.GetMainUnit("92").Result;
90+
Assert.AreEqual("91", r?.OrganizationNumber);
91+
}
92+
93+
[TestMethod]
94+
public void TestGetFullMainAttemptsSubunitLookup()
95+
{
96+
var s = new EntityRegistryService(_entityRegistryApiClientServiceMock.Object) { UseCoreProxy = false };
97+
var r = s.GetFullMainUnit("92").Result;
98+
Assert.AreEqual("91", r?.Organisasjonsnummer);
99+
}
100+
101+
[TestMethod]
102+
public void TestGetSubUnitOnlyReturnsSubunit()
103+
{
104+
var s = new EntityRegistryService(_entityRegistryApiClientServiceMock.Object) { UseCoreProxy = false };
105+
var r = s.Get("92", subUnitOnly: true).Result;
106+
Assert.AreEqual("92", r?.OrganizationNumber);
107+
}
108+
109+
[TestMethod]
110+
public void TestGetSubUnitOnlyReturnsNullIfMainUnit()
111+
{
112+
var s = new EntityRegistryService(_entityRegistryApiClientServiceMock.Object) { UseCoreProxy = false };
113+
var r = s.Get("91", subUnitOnly: true).Result;
114+
Assert.IsNull(r);
115+
}
116+
117+
[TestMethod]
118+
public void TestGetNestToTopmostMainUnitReturnsMainunit()
119+
{
120+
var s = new EntityRegistryService(_entityRegistryApiClientServiceMock.Object) { UseCoreProxy = false };
121+
var r = s.GetMainUnit("94").Result;
122+
Assert.AreEqual("91", r?.OrganizationNumber);
123+
}
124+
125+
[TestMethod]
126+
public void TestSyntheticLookupsNotAllowedByDefault()
127+
{
128+
var s = new EntityRegistryService(_entityRegistryApiClientServiceMock.Object) { UseCoreProxy = false };
129+
var r = s.GetMainUnit("31").Result;
130+
Assert.IsNull(r);
131+
}
132+
133+
[TestMethod]
134+
public void TestIsMainUnit()
135+
{
136+
var s = new EntityRegistryService(_entityRegistryApiClientServiceMock.Object) { UseCoreProxy = false };
137+
var r = s.IsMainUnit("91").Result;
138+
Assert.IsTrue(r);
139+
}
140+
141+
[TestMethod]
142+
public void TestIsMainUnitSimpleObj()
143+
{
144+
var s = new EntityRegistryService(_entityRegistryApiClientServiceMock.Object) { UseCoreProxy = false };
145+
var r = s.IsMainUnit(new SimpleEntityRegistryUnit());
146+
Assert.IsTrue(r);
147+
}
148+
149+
[TestMethod]
150+
public void TestIsMainUnitFullObj()
151+
{
152+
var s = new EntityRegistryService(_entityRegistryApiClientServiceMock.Object) { UseCoreProxy = false };
153+
var r = s.IsMainUnit(new EntityRegistryUnit { Organisasjonsnummer = "91", Organisasjonsform = new Organisasjonsform { Kode = "AS" } });
154+
Assert.IsTrue(r);
155+
}
156+
157+
[TestMethod]
158+
public void TestIsSubUnit()
159+
{
160+
var s = new EntityRegistryService(_entityRegistryApiClientServiceMock.Object) { UseCoreProxy = false };
161+
var r = s.IsSubUnit("92").Result;
162+
Assert.IsTrue(r);
163+
}
164+
165+
[TestMethod]
166+
public void TestIsSubUnitSimpleObj()
167+
{
168+
var s = new EntityRegistryService(_entityRegistryApiClientServiceMock.Object) { UseCoreProxy = false };
169+
var r = s.IsSubUnit(new SimpleEntityRegistryUnit { ParentUnit = "x" });
170+
Assert.IsTrue(r);
171+
}
172+
173+
[TestMethod]
174+
public void TestIsSubUnitFullObj()
175+
{
176+
var s = new EntityRegistryService(_entityRegistryApiClientServiceMock.Object) { UseCoreProxy = false };
177+
var r = s.IsSubUnit(new EntityRegistryUnit { Organisasjonsnummer = "91", Organisasjonsform = new Organisasjonsform { Kode = "AS" }, OverordnetEnhet = "x" });
178+
Assert.IsTrue(r);
179+
}
180+
181+
[DataTestMethod]
182+
[DataRow("971032146", "AS", "0000", "0000", true)] // Public sector organization from PublicSectorOrganizations
183+
[DataRow("123456789", "KF", "0000", "0000", true)] // Public sector unit type
184+
[DataRow("123456789", "AS", "3900", "0000", true)] // Public sector sector code
185+
[DataRow("123456789", "AS", "0000", "8411", true)] // Public sector industrial code
186+
[DataRow("123456789", "AS", "0000", "0000", false)] // Non-public sector organization
187+
public void TestIsPublicAgency(
188+
string organizationNumber,
189+
string organizationForm,
190+
string sectorCode,
191+
string industrialCode1,
192+
bool expectedResult)
193+
{
194+
// Arrange
195+
var entityRegistryUnit = new SimpleEntityRegistryUnit
196+
{
197+
OrganizationNumber = organizationNumber,
198+
OrganizationForm = organizationForm,
199+
SectorCode = sectorCode,
200+
IndustrialCodes = new List<string> { industrialCode1 }
201+
};
202+
203+
// Act
204+
var isPublicAgency = _entityRegistryService.IsPublicAgency(entityRegistryUnit);
205+
206+
// Assert
207+
Assert.AreEqual(expectedResult, isPublicAgency);
208+
}
209+
}

Dan.Common.UnitTest/Usings.cs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
global using Microsoft.VisualStudio.TestTools.UnitTesting;
2+
global using System.Diagnostics.CodeAnalysis;
3+
global using Dan.Common.Interfaces;
4+
global using Dan.Common.Models;
5+
global using Dan.Common.Services;
6+
global using Moq;

Dan.Common/Services/EntityRegistryService.cs

+29-17
Original file line numberDiff line numberDiff line change
@@ -59,32 +59,44 @@ public EntityRegistryService(IEntityRegistryApiClientService entityRegistryApiCl
5959
}
6060

6161
EntityRegistryUnit? unit;
62+
// We only want a subunit, so try that and return regardless
6263
if (subUnitOnly)
6364
{
64-
unit = await InternalGet(organizationNumber, UnitType.SubUnit);
65-
if (unit == null) return null;
66-
67-
if (nestToAndReturnMainUnit && unit.OverordnetEnhet != null)
68-
{
69-
var parentUnit = unit;
70-
do
71-
{
72-
parentUnit = await InternalGet(parentUnit.OverordnetEnhet, UnitType.MainUnit);
73-
}
74-
while (parentUnit!.OverordnetEnhet != null);
75-
76-
return parentUnit;
77-
}
65+
return await InternalGet(organizationNumber, UnitType.SubUnit);
7866
}
7967

68+
// At this point we return a mainunit if we find one
8069
unit = await InternalGet(organizationNumber, UnitType.MainUnit);
81-
if (unit == null && attemptSubUnitLookupIfNotFound)
70+
if (unit != null)
8271
{
83-
return await InternalGet(organizationNumber, UnitType.SubUnit);
72+
return unit;
73+
}
74+
75+
// Didn't find a main unit, check if we should check if it's a subunit
76+
// or nest to topmost parent
77+
if (attemptSubUnitLookupIfNotFound || nestToAndReturnMainUnit) {
78+
unit = await InternalGet(organizationNumber, UnitType.SubUnit);
8479
}
80+
81+
// If we didn't find any subunit at this point or we're not supposed
82+
// to nest, return at this point
83+
if (unit == null || !nestToAndReturnMainUnit)
84+
{
85+
return unit;
86+
}
87+
88+
// We did find a subunit, and we're instructed to nest to the top mainunit
89+
var parentUnit = unit;
90+
do
91+
{
92+
// Only subunits are at the leaf node, any nested parents are MainUnits
93+
// Example: https://data.brreg.no/enhetsregisteret/api/underenheter/879587662
94+
parentUnit = await InternalGet(parentUnit.OverordnetEnhet!, UnitType.MainUnit);
95+
}
96+
while (parentUnit?.OverordnetEnhet != null);
97+
unit = parentUnit;
8598

8699
return unit;
87-
88100
}
89101

90102
public async Task<EntityRegistryUnit?> GetFullMainUnit(string organizationNumber) => await GetFull(organizationNumber, attemptSubUnitLookupIfNotFound: false, nestToAndReturnMainUnit: true);

Dan.sln

+6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dan.Common", "Dan.Common\Da
1212
EndProject
1313
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dan.Core.UnitTest", "Dan.Core.UnitTest\Dan.Core.UnitTest.csproj", "{CD43EC26-1570-475D-8A05-4D8CC4BB6810}"
1414
EndProject
15+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dan.Common.UnitTest", "Dan.Common.UnitTest\Dan.Common.UnitTest.csproj", "{9B7CB8FD-7827-48D6-895E-0F79C27FD0EE}"
16+
EndProject
1517
Global
1618
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1719
Debug|Any CPU = Debug|Any CPU
@@ -30,6 +32,10 @@ Global
3032
{CD43EC26-1570-475D-8A05-4D8CC4BB6810}.Debug|Any CPU.Build.0 = Debug|Any CPU
3133
{CD43EC26-1570-475D-8A05-4D8CC4BB6810}.Release|Any CPU.ActiveCfg = Release|Any CPU
3234
{CD43EC26-1570-475D-8A05-4D8CC4BB6810}.Release|Any CPU.Build.0 = Release|Any CPU
35+
{9B7CB8FD-7827-48D6-895E-0F79C27FD0EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
36+
{9B7CB8FD-7827-48D6-895E-0F79C27FD0EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
37+
{9B7CB8FD-7827-48D6-895E-0F79C27FD0EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
38+
{9B7CB8FD-7827-48D6-895E-0F79C27FD0EE}.Release|Any CPU.Build.0 = Release|Any CPU
3339
EndGlobalSection
3440
GlobalSection(SolutionProperties) = preSolution
3541
HideSolutionNode = FALSE

0 commit comments

Comments
 (0)