Skip to content

Commit 76c01c2

Browse files
committed
feat: Unit tests for HighNeedsService so far, plus coverage for LocalAuthorityCurrentFinancialQuery
1 parent 17e94f8 commit 76c01c2

File tree

7 files changed

+228
-107
lines changed

7 files changed

+228
-107
lines changed

core-infrastructure/src/db/Core.Database/Views/018-LocalAuthorityFinancial.sql

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ AS
9696
IIF(Population2To18 > 0.0, OutturnPlaceFundingPrimary / Population2To18, NULL) AS OutturnPlaceFundingPrimary,
9797
IIF(Population2To18 > 0.0, OutturnPlaceFundingSecondary / Population2To18, NULL) AS OutturnPlaceFundingSecondary,
9898
IIF(Population2To18 > 0.0, OutturnPlaceFundingSpecial / Population2To18, NULL) AS OutturnPlaceFundingSpecial,
99-
IIF(Population2To18 > 0.0, OutturnPlaceFundingAlternativeProvision / Population2To18, NULL) AS TotalExOutturnPlaceFundingAlternativeProvisionpenditure,
99+
IIF(Population2To18 > 0.0, OutturnPlaceFundingAlternativeProvision / Population2To18, NULL) AS OutturnPlaceFundingAlternativeProvision,
100100
IIF(Population2To18 > 0.0, BudgetTotalHighNeeds / Population2To18, NULL) AS BudgetTotalHighNeeds,
101101
IIF(Population2To18 > 0.0, BudgetTotalPlaceFunding / Population2To18, NULL) AS BudgetTotalPlaceFunding,
102102
IIF(Population2To18 > 0.0, BudgetTotalTopUpFundingMaintained / Population2To18, NULL) AS BudgetTotalTopUpFundingMaintained,

platform/src/abstractions/Platform.Sql/DatabaseConnection.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public interface IDatabaseConnection : IDbConnection
126126

127127
/// <inheritdoc
128128
/// cref="Dapper.SqlMapper.QueryAsync&lt;TReturn&gt;(IDbConnection, string, Type[], Func&lt;object[], TReturn&gt;, object?, IDbTransaction?, bool, string, int?, CommandType?)" />
129-
/// <remarks>No <see cref="CancellationToken" /> support (see https://github.com/DapperLib/Dapper/issues/2125).</remarks>
129+
/// <remarks>No <see cref="CancellationToken" /> support (see https://github.com/DapperLib/Dapper/issues/2125).</remarks>
130130
Task<IEnumerable<TReturn>> QueryAsync<TReturn>(PlatformQuery query, Type[] types, Func<object[], TReturn> map, string[] splitOn);
131131

132132
/// <inheritdoc cref="Dapper.SqlMapper.QueryFirstOrDefaultAsync&lt;T&gt;(IDbConnection, CommandDefinition)" />

platform/src/abstractions/tests/Platform.Sql.Tests/Builders/LocalAuthorityQueryTests.cs

+27
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,31 @@ public void ShouldReturnSql()
1313
}
1414

1515
private static LocalAuthorityQuery Create() => new();
16+
}
17+
18+
public class LocalAuthorityCurrentFinancialQueryTests
19+
{
20+
[Theory]
21+
[MemberData(nameof(Data))]
22+
public void ShouldReturnSql(string dimension, string[] fields, string expected)
23+
{
24+
var builder = Create(dimension, fields);
25+
Assert.Equal(expected, builder.QueryTemplate.RawSql);
26+
}
27+
28+
[Fact]
29+
public void ShouldThrowArgumentOutOfRangeException()
30+
{
31+
Assert.Throws<ArgumentOutOfRangeException>(() => Create("dimension", []));
32+
}
33+
34+
public static TheoryData<string, string[], string> Data => new()
35+
{
36+
{ "Actuals", [], "SELECT * FROM VW_LocalAuthorityFinancialDefaultCurrentActual " },
37+
{ "Actuals", ["field1", "field2"], "SELECT field1 , field2\n FROM VW_LocalAuthorityFinancialDefaultCurrentActual " },
38+
{ "PerHead", [], "SELECT * FROM VW_LocalAuthorityFinancialDefaultCurrentPerPopulation " },
39+
{ "PerHead", ["field1", "field2"], "SELECT field1 , field2\n FROM VW_LocalAuthorityFinancialDefaultCurrentPerPopulation " },
40+
};
41+
42+
private static LocalAuthorityCurrentFinancialQuery Create(string dimension, string[] fields) => new(dimension, fields);
1643
}

platform/src/apis/Platform.Api.LocalAuthorityFinances/Features/HighNeeds/Services/HighNeedsService.cs

+83-83
Original file line numberDiff line numberDiff line change
@@ -21,70 +21,70 @@ public class HighNeedsService(IDatabaseFactory dbFactory) : HighNeedsStubService
2121
string[] fields =
2222
[
2323
// LocalAuthorityBase
24-
"LaCode AS Code",
25-
"Name",
24+
"LaCode AS [Code]",
25+
"[Name]",
2626
// HighNeedsBase
27-
"OutturnTotalHighNeeds AS Total",
27+
"OutturnTotalHighNeeds AS [Total]",
2828
// HighNeedsAmount
29-
"OutturnTotalPlaceFunding AS TotalPlaceFunding",
30-
"OutturnTotalTopUpFundingMaintained AS TopUpFundingMaintained",
31-
"OutturnTotalTopUpFundingNonMaintained AS TopUpFundingNonMaintained",
32-
"OutturnTotalSenServices AS SenServices",
33-
"OutturnTotalAlternativeProvisionServices AS AlternativeProvisionServices",
34-
"OutturnTotalHospitalServices AS HospitalServices",
35-
"OutturnTotalOtherHealthServices AS OtherHealthServices",
29+
"OutturnTotalPlaceFunding AS [TotalPlaceFunding]",
30+
"OutturnTotalTopUpFundingMaintained AS [TopUpFundingMaintained]",
31+
"OutturnTotalTopUpFundingNonMaintained AS [TopUpFundingNonMaintained]",
32+
"OutturnTotalSenServices AS [SenServices]",
33+
"OutturnTotalAlternativeProvisionServices AS [AlternativeProvisionServices]",
34+
"OutturnTotalHospitalServices AS [HospitalServices]",
35+
"OutturnTotalOtherHealthServices AS [OtherHealthServices]",
3636
// TopFunding
37-
"OutturnTopFundingMaintainedEarlyYears AS EarlyYears",
37+
"OutturnTopFundingMaintainedEarlyYears AS [EarlyYears]",
3838
"OutturnTopFundingMaintainedPrimary AS [Primary]",
39-
"OutturnTopFundingMaintainedSecondary AS Secondary",
40-
"OutturnTopFundingMaintainedSpecial AS Special",
41-
"OutturnTopFundingMaintainedAlternativeProvision AS AlternativeProvision",
42-
"OutturnTopFundingMaintainedPostSchool AS PostSchool",
43-
"OutturnTopFundingMaintainedIncome AS Income",
39+
"OutturnTopFundingMaintainedSecondary AS [Secondary]",
40+
"OutturnTopFundingMaintainedSpecial AS [Special]",
41+
"OutturnTopFundingMaintainedAlternativeProvision AS [AlternativeProvision]",
42+
"OutturnTopFundingMaintainedPostSchool AS [PostSchool]",
43+
"OutturnTopFundingMaintainedIncome AS [Income]",
4444
// TopFunding
45-
"OutturnTopFundingNonMaintainedEarlyYears AS EarlyYears",
45+
"OutturnTopFundingNonMaintainedEarlyYears AS [EarlyYears]",
4646
"OutturnTopFundingNonMaintainedPrimary AS [Primary]",
47-
"OutturnTopFundingNonMaintainedSecondary AS Secondary",
48-
"OutturnTopFundingNonMaintainedSpecial AS Special",
49-
"OutturnTopFundingNonMaintainedAlternativeProvision AS AlternativeProvision",
50-
"OutturnTopFundingNonMaintainedPostSchool AS PostSchool",
51-
"OutturnTopFundingNonMaintainedIncome AS Income",
47+
"OutturnTopFundingNonMaintainedSecondary AS [Secondary]",
48+
"OutturnTopFundingNonMaintainedSpecial AS [Special]",
49+
"OutturnTopFundingNonMaintainedAlternativeProvision AS [AlternativeProvision]",
50+
"OutturnTopFundingNonMaintainedPostSchool AS [PostSchool]",
51+
"OutturnTopFundingNonMaintainedIncome AS [Income]",
5252
// PlaceFunding
5353
"OutturnPlaceFundingPrimary AS [Primary]",
54-
"OutturnPlaceFundingSecondary AS Secondary",
55-
"OutturnPlaceFundingSpecial AS Special",
56-
"OutturnPlaceFundingAlternativeProvision AS AlternativeProvision",
54+
"OutturnPlaceFundingSecondary AS [Secondary]",
55+
"OutturnPlaceFundingSpecial AS [Special]",
56+
"OutturnPlaceFundingAlternativeProvision AS [AlternativeProvision]",
5757
// HighNeedsBase
58-
"BudgetTotalHighNeeds AS Total",
58+
"BudgetTotalHighNeeds AS [Total]",
5959
// HighNeedsAmount
60-
"BudgetTotalPlaceFunding AS TotalPlaceFunding",
61-
"BudgetTotalTopUpFundingMaintained AS TopUpFundingMaintained",
62-
"BudgetTotalTopUpFundingNonMaintained AS TopUpFundingNonMaintained",
63-
"BudgetTotalSenServices AS SenServices",
64-
"BudgetTotalAlternativeProvisionServices AS AlternativeProvisionServices",
65-
"BudgetTotalHospitalServices AS HospitalServices",
66-
"BudgetTotalOtherHealthServices AS OtherHealthServices",
60+
"BudgetTotalPlaceFunding AS [TotalPlaceFunding]",
61+
"BudgetTotalTopUpFundingMaintained AS [TopUpFundingMaintained]",
62+
"BudgetTotalTopUpFundingNonMaintained AS [TopUpFundingNonMaintained]",
63+
"BudgetTotalSenServices AS [SenServices]",
64+
"BudgetTotalAlternativeProvisionServices AS [AlternativeProvisionServices]",
65+
"BudgetTotalHospitalServices AS [HospitalServices]",
66+
"BudgetTotalOtherHealthServices AS [OtherHealthServices]",
6767
// TopFunding
68-
"BudgetTopFundingMaintainedEarlyYears AS EarlyYears",
68+
"BudgetTopFundingMaintainedEarlyYears AS [EarlyYears]",
6969
"BudgetTopFundingMaintainedPrimary AS [Primary]",
70-
"BudgetTopFundingMaintainedSecondary AS Secondary",
71-
"BudgetTopFundingMaintainedSpecial AS Special",
72-
"BudgetTopFundingMaintainedAlternativeProvision AS AlternativeProvision",
73-
"BudgetTopFundingMaintainedPostSchool AS PostSchool",
74-
"BudgetTopFundingMaintainedIncome AS Income",
70+
"BudgetTopFundingMaintainedSecondary AS [Secondary]",
71+
"BudgetTopFundingMaintainedSpecial AS [Special]",
72+
"BudgetTopFundingMaintainedAlternativeProvision AS [AlternativeProvision]",
73+
"BudgetTopFundingMaintainedPostSchool AS [PostSchool]",
74+
"BudgetTopFundingMaintainedIncome AS [Income]",
7575
// TopFunding
76-
"BudgetTopFundingNonMaintainedEarlyYears AS EarlyYears",
76+
"BudgetTopFundingNonMaintainedEarlyYears AS [EarlyYears]",
7777
"BudgetTopFundingNonMaintainedPrimary AS [Primary]",
78-
"BudgetTopFundingNonMaintainedSecondary AS Secondary",
79-
"BudgetTopFundingNonMaintainedSpecial AS Special",
80-
"BudgetTopFundingNonMaintainedAlternativeProvision AS AlternativeProvision",
81-
"BudgetTopFundingNonMaintainedPostSchool AS PostSchool",
82-
"BudgetTopFundingNonMaintainedIncome AS Income",
78+
"BudgetTopFundingNonMaintainedSecondary AS [Secondary]",
79+
"BudgetTopFundingNonMaintainedSpecial AS [Special]",
80+
"BudgetTopFundingNonMaintainedAlternativeProvision AS [AlternativeProvision]",
81+
"BudgetTopFundingNonMaintainedPostSchool AS [PostSchool]",
82+
"BudgetTopFundingNonMaintainedIncome AS [Income]",
8383
// PlaceFunding
8484
"BudgetPlaceFundingPrimary AS [Primary]",
85-
"BudgetPlaceFundingSecondary AS Secondary",
86-
"BudgetPlaceFundingSpecial AS Special",
87-
"BudgetPlaceFundingAlternativeProvision AS AlternativeProvision"
85+
"BudgetPlaceFundingSecondary AS [Secondary]",
86+
"BudgetPlaceFundingSpecial AS [Special]",
87+
"BudgetPlaceFundingAlternativeProvision AS [AlternativeProvision]"
8888
];
8989

9090
Type[] types =
@@ -120,44 +120,44 @@ public class HighNeedsService(IDatabaseFactory dbFactory) : HighNeedsStubService
120120
var laBuilder = new LocalAuthorityCurrentFinancialQuery(dimension, fields)
121121
.WhereLaCodesIn(codes);
122122

123-
var results = await conn.QueryAsync(laBuilder, types, Map, splitOn);
123+
var results = await conn.QueryAsync(laBuilder, types, MultiMapToHighNeeds, splitOn);
124124
return results.ToArray();
125+
}
125126

126-
LocalAuthority<Models.HighNeeds> Map(object[] objects)
127-
{
128-
var localAuthority = objects[0] as LocalAuthorityBase;
129-
var outturn = objects[1] as HighNeedsBase;
130-
var outturnHighNeedsAmount = objects[2] as HighNeedsAmount;
131-
var outturnTopFundingMaintained = objects[3] as TopFunding;
132-
var outturnTopFundingNonMaintained = objects[4] as TopFunding;
133-
var outturnPlaceFunding = objects[5] as PlaceFunding;
134-
var budget = objects[6] as HighNeedsBase;
135-
var budgetHighNeedsAmount = objects[7] as HighNeedsAmount;
136-
var budgeTopFundingMaintained = objects[8] as TopFunding;
137-
var budgetTopFundingNonMaintained = objects[9] as TopFunding;
138-
var budgetPlaceFunding = objects[10] as PlaceFunding;
127+
internal static LocalAuthority<Models.HighNeeds> MultiMapToHighNeeds(object[] objects)
128+
{
129+
var localAuthority = objects[0] as LocalAuthorityBase;
130+
var outturn = objects[1] as HighNeedsBase;
131+
var outturnHighNeedsAmount = objects[2] as HighNeedsAmount;
132+
var outturnTopFundingMaintained = objects[3] as TopFunding;
133+
var outturnTopFundingNonMaintained = objects[4] as TopFunding;
134+
var outturnPlaceFunding = objects[5] as PlaceFunding;
135+
var budget = objects[6] as HighNeedsBase;
136+
var budgetHighNeedsAmount = objects[7] as HighNeedsAmount;
137+
var budgetTopFundingMaintained = objects[8] as TopFunding;
138+
var budgetTopFundingNonMaintained = objects[9] as TopFunding;
139+
var budgetPlaceFunding = objects[10] as PlaceFunding;
139140

140-
return new LocalAuthority<Models.HighNeeds>
141+
return new LocalAuthority<Models.HighNeeds>
142+
{
143+
Code = localAuthority?.Code,
144+
Name = localAuthority?.Name,
145+
Outturn = new Models.HighNeeds
146+
{
147+
Total = outturn?.Total,
148+
HighNeedsAmount = outturnHighNeedsAmount,
149+
Maintained = outturnTopFundingMaintained,
150+
NonMaintained = outturnTopFundingNonMaintained,
151+
PlaceFunding = outturnPlaceFunding
152+
},
153+
Budget = new Models.HighNeeds
141154
{
142-
Code = localAuthority?.Code,
143-
Name = localAuthority?.Name,
144-
Outturn = new Models.HighNeeds
145-
{
146-
Total = outturn?.Total,
147-
HighNeedsAmount = outturnHighNeedsAmount,
148-
Maintained = outturnTopFundingMaintained,
149-
NonMaintained = outturnTopFundingNonMaintained,
150-
PlaceFunding = outturnPlaceFunding
151-
},
152-
Budget = new Models.HighNeeds
153-
{
154-
Total = budget?.Total,
155-
HighNeedsAmount = budgetHighNeedsAmount,
156-
Maintained = budgeTopFundingMaintained,
157-
NonMaintained = budgetTopFundingNonMaintained,
158-
PlaceFunding = budgetPlaceFunding
159-
}
160-
};
161-
}
155+
Total = budget?.Total,
156+
HighNeedsAmount = budgetHighNeedsAmount,
157+
Maintained = budgetTopFundingMaintained,
158+
NonMaintained = budgetTopFundingNonMaintained,
159+
PlaceFunding = budgetPlaceFunding
160+
}
161+
};
162162
}
163163
}

platform/src/apis/Platform.Api.LocalAuthorityFinances/Features/HighNeeds/Services/HighNeedsStubService.cs

+3-22
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.Diagnostics.CodeAnalysis;
34
using System.Linq;
45
using System.Threading;
@@ -12,10 +13,7 @@ namespace Platform.Api.LocalAuthorityFinances.Features.HighNeeds.Services;
1213
[SuppressMessage("Performance", "CA1822:Mark members as static")]
1314
public class HighNeedsStubService : IHighNeedsService
1415
{
15-
public virtual Task<LocalAuthority<Models.HighNeeds>[]> Get(string[] codes, string dimension, CancellationToken cancellationToken = default)
16-
{
17-
return Task.FromResult(codes.Select(c => GetLocalAuthority(c, dimension)).ToArray());
18-
}
16+
public virtual Task<LocalAuthority<Models.HighNeeds>[]> Get(string[] codes, string dimension, CancellationToken cancellationToken = default) => throw new NotImplementedException();
1917

2018
public virtual Task<History<HighNeedsYear>?> GetHistory(string[] codes, CancellationToken cancellationToken = default)
2119
{
@@ -92,21 +90,4 @@ private static IEnumerable<HighNeedsYear> GetOutturn(string code, int startYear,
9290
AlternativeProvision = baseValue + 25 + year
9391
}
9492
};
95-
96-
private static LocalAuthority<Models.HighNeeds> GetLocalAuthority(string code, string dimension)
97-
{
98-
if (!int.TryParse(code, out var baseValue))
99-
{
100-
baseValue = code.Length;
101-
}
102-
103-
var baseMultiplier = dimension == "Actuals" ? 1_000 : 1;
104-
return new LocalAuthority<Models.HighNeeds>
105-
{
106-
Code = code,
107-
Name = $"Local authority {code}",
108-
Budget = GetStubbedRow(code, 2024, 1_100 * baseMultiplier + baseValue, 1_110 * baseMultiplier + baseValue % 2),
109-
Outturn = GetStubbedRow(code, 2024, 1_000 * baseMultiplier + baseValue, 1_010 * baseMultiplier + baseValue % 2)
110-
};
111-
}
11293
}

platform/src/apis/Platform.Api.LocalAuthorityFinances/Program.cs

+2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
using System.Diagnostics.CodeAnalysis;
2+
using System.Runtime.CompilerServices;
23
using Microsoft.Azure.Functions.Worker.Extensions.OpenApi.Extensions;
34
using Microsoft.Extensions.Hosting;
45
using Platform.Api.LocalAuthorityFinances.Configuration;
56

7+
[assembly: InternalsVisibleTo("Platform.LocalAuthorityFinances.Tests")]
68
var hostBuilder = new HostBuilder()
79
.ConfigureFunctionsWorkerDefaults(Worker.Configure, Worker.Options)
810
.ConfigureServices(Services.Configure)

0 commit comments

Comments
 (0)