From de14ef62a73a8dc1a2368306d3e864351be193cc Mon Sep 17 00:00:00 2001 From: Farshad DASHTI Date: Fri, 9 Aug 2024 16:22:30 +0100 Subject: [PATCH 1/2] Added integration tests for Persons API --- .../ConstituenciesControllerTests.cs | 48 +++++++ ...demies.PersonsApi.Tests.Integration.csproj | 33 +++++ .../Mocks/CustomWebApplicationFactory.cs | 118 ++++++++++++++++++ TramsDataApi.sln | 7 ++ 4 files changed, 206 insertions(+) create mode 100644 Dfe.Academies.PersonsApi.Tests.Integration/Controllers/ConstituenciesControllerTests.cs create mode 100644 Dfe.Academies.PersonsApi.Tests.Integration/Dfe.Academies.PersonsApi.Tests.Integration.csproj create mode 100644 Dfe.Academies.PersonsApi.Tests.Integration/Mocks/CustomWebApplicationFactory.cs diff --git a/Dfe.Academies.PersonsApi.Tests.Integration/Controllers/ConstituenciesControllerTests.cs b/Dfe.Academies.PersonsApi.Tests.Integration/Controllers/ConstituenciesControllerTests.cs new file mode 100644 index 000000000..77b566d4d --- /dev/null +++ b/Dfe.Academies.PersonsApi.Tests.Integration/Controllers/ConstituenciesControllerTests.cs @@ -0,0 +1,48 @@ +using Dfe.Academies.PersonsApi.Tests.Integration.Mocks; +using Microsoft.EntityFrameworkCore; +using PersonsApi; +using System.Net; + +namespace Dfe.Academies.PersonsApi.Tests.Integration.Controllers +{ + public class When_Fetching_Mp_By_Constituency : IClassFixture> + { + private readonly CustomWebApplicationFactory _factory; + + public When_Fetching_Mp_By_Constituency(CustomWebApplicationFactory factory) + { + _factory = factory; + } + + [Fact] + public async Task it_should_return_mp_when_constituency_exist() + { + var client = _factory.CreateClient(); + + var dbcontext = _factory.GetDbContext(); + + await dbcontext.Constituencies.Where(x => x.ConstituencyName == "Test Constituency 1") + .ExecuteUpdateAsync(x => x.SetProperty(p => p.ConstituencyName, "NewConstituencyName")); + + var constituencyName = Uri.EscapeDataString("NewConstituencyName"); + + var response = await client.GetAsync($"v1/Constituencies/{constituencyName}/mp"); + + var content = await response.Content.ReadAsStringAsync(); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + + [Fact] + public async Task it_should_return_notfound_when_constituency_doesnt_exist() + { + var client = _factory.CreateClient(); + + var response = await client.GetAsync($"v1/Constituencies/test/mp"); + + var content = await response.Content.ReadAsStringAsync(); + + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + } +} diff --git a/Dfe.Academies.PersonsApi.Tests.Integration/Dfe.Academies.PersonsApi.Tests.Integration.csproj b/Dfe.Academies.PersonsApi.Tests.Integration/Dfe.Academies.PersonsApi.Tests.Integration.csproj new file mode 100644 index 000000000..eea5821c2 --- /dev/null +++ b/Dfe.Academies.PersonsApi.Tests.Integration/Dfe.Academies.PersonsApi.Tests.Integration.csproj @@ -0,0 +1,33 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Dfe.Academies.PersonsApi.Tests.Integration/Mocks/CustomWebApplicationFactory.cs b/Dfe.Academies.PersonsApi.Tests.Integration/Mocks/CustomWebApplicationFactory.cs new file mode 100644 index 000000000..df98ea61a --- /dev/null +++ b/Dfe.Academies.PersonsApi.Tests.Integration/Mocks/CustomWebApplicationFactory.cs @@ -0,0 +1,118 @@ +using Dfe.Academies.Academisation.Data; +using Dfe.Academies.Domain.Persons; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using System.Data.Common; + +namespace Dfe.Academies.PersonsApi.Tests.Integration.Mocks +{ + public class CustomWebApplicationFactory + : WebApplicationFactory where TProgram : class + { + private SqliteConnection _connection; + + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + builder.ConfigureServices(services => + { + var dbContextDescriptor = services.SingleOrDefault( + d => d.ServiceType == + typeof(DbContextOptions)); + + services.Remove(dbContextDescriptor!); + + var dbConnectionDescriptor = services.SingleOrDefault( + d => d.ServiceType == + typeof(DbConnection)); + + services.Remove(dbConnectionDescriptor!); + + services.AddSingleton(container => + { + if (_connection == null) + { + _connection = new SqliteConnection("DataSource=:memory:"); + _connection.Open(); + } + + return _connection; + }); + + services.AddDbContext((container, options) => + { + var connection = container.GetRequiredService(); + options.UseSqlite(connection); + }); + + var serviceProvider = services.BuildServiceProvider(); + + using (var scope = serviceProvider.CreateScope()) + { + var db = scope.ServiceProvider.GetRequiredService(); + + db.Database.EnsureCreated(); + + SeedTestData(db); + } + }); + + builder.UseEnvironment("Development"); + } + + public MopContext GetDbContext() + { + var scopeFactory = Services.GetRequiredService(); + var scope = scopeFactory.CreateScope(); + return scope.ServiceProvider.GetRequiredService(); + } + + private void SeedTestData(MopContext context) + { + context.MemberContactDetails.Add(new MemberContactDetails + { + MemberID = 1, + Email = "test1@example.com", + TypeId = 1 + }); + context.MemberContactDetails.Add(new MemberContactDetails + { + MemberID = 2, + Email = "test2@example.com", + TypeId = 2 + }); + + context.Constituencies.Add(new Constituency + { + ConstituencyId = 1, + ConstituencyName = "Test Constituency 1", + NameList = "Wood, John", + NameDisplayAs = "John Wood", + NameFullTitle = "John Wood MP", + LastRefresh = DateTime.UtcNow, + MemberID = 1 + }); + context.Constituencies.Add(new Constituency + { + ConstituencyId = 2, + ConstituencyName = "Test Constituency 2", + NameList = "Wood, Joe", + NameDisplayAs = "Joe Wood", + NameFullTitle = "Joe Wood MP", + LastRefresh = DateTime.UtcNow, + MemberID= 2 + }); + + context.SaveChanges(); + } + + protected override void ConfigureClient(HttpClient client) + { + client.DefaultRequestHeaders.Add("ApiKey", "app-key"); + + base.ConfigureClient(client); + } + } +} diff --git a/TramsDataApi.sln b/TramsDataApi.sln index 963fcbaba..d94d2f6f5 100644 --- a/TramsDataApi.sln +++ b/TramsDataApi.sln @@ -24,6 +24,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dfe.Academies.Utils", "Dfe. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PersonsApi", "PersonsApi\PersonsApi.csproj", "{039FE264-0819-409A-8E01-53CA082812B3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dfe.Academies.PersonsApi.Tests.Integration", "Dfe.Academies.PersonsApi.Tests.Integration\Dfe.Academies.PersonsApi.Tests.Integration.csproj", "{693F82DB-BD57-48C0-B558-783153354DA8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -58,12 +60,17 @@ Global {039FE264-0819-409A-8E01-53CA082812B3}.Debug|Any CPU.Build.0 = Debug|Any CPU {039FE264-0819-409A-8E01-53CA082812B3}.Release|Any CPU.ActiveCfg = Release|Any CPU {039FE264-0819-409A-8E01-53CA082812B3}.Release|Any CPU.Build.0 = Release|Any CPU + {693F82DB-BD57-48C0-B558-783153354DA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {693F82DB-BD57-48C0-B558-783153354DA8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {693F82DB-BD57-48C0-B558-783153354DA8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {693F82DB-BD57-48C0-B558-783153354DA8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {AF49D395-A01A-43E9-A643-6B2266BA62C5} = {B3CDCA56-9765-4C2B-A4A4-2738C5A268EB} + {693F82DB-BD57-48C0-B558-783153354DA8} = {B3CDCA56-9765-4C2B-A4A4-2738C5A268EB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F0704299-A9C2-448A-B816-E5BCCB345AF8} From 58f0e72992a6be972bc2a90fe47d450a5107dc44 Mon Sep 17 00:00:00 2001 From: Farshad DASHTI Date: Fri, 9 Aug 2024 16:27:33 +0100 Subject: [PATCH 2/2] General refactoring --- .../Mocks/CustomWebApplicationFactory.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dfe.Academies.PersonsApi.Tests.Integration/Mocks/CustomWebApplicationFactory.cs b/Dfe.Academies.PersonsApi.Tests.Integration/Mocks/CustomWebApplicationFactory.cs index df98ea61a..c4a7fa73f 100644 --- a/Dfe.Academies.PersonsApi.Tests.Integration/Mocks/CustomWebApplicationFactory.cs +++ b/Dfe.Academies.PersonsApi.Tests.Integration/Mocks/CustomWebApplicationFactory.cs @@ -12,7 +12,7 @@ namespace Dfe.Academies.PersonsApi.Tests.Integration.Mocks public class CustomWebApplicationFactory : WebApplicationFactory where TProgram : class { - private SqliteConnection _connection; + private SqliteConnection? _connection; protected override void ConfigureWebHost(IWebHostBuilder builder) { @@ -69,7 +69,7 @@ public MopContext GetDbContext() return scope.ServiceProvider.GetRequiredService(); } - private void SeedTestData(MopContext context) + private static void SeedTestData(MopContext context) { context.MemberContactDetails.Add(new MemberContactDetails {