From fe71997c0bf702e7cee5d3abb4843d3a8aa0f29a Mon Sep 17 00:00:00 2001 From: Simon Geering <25039878+SimonGeering@users.noreply.github.com> Date: Thu, 8 Apr 2021 16:35:20 +0100 Subject: [PATCH] #167 Updated IntegrationTestBase and DbContext SeedData code to allow the Respawn NuGet package to clear down Currency and BankAccount seed data and re-populate it when running tests. --- .../EntityFramework/Model/AccountsSchema.cs | 33 ++++++-- .../DAL/EntityFramework/Model/CoreSchema.cs | 33 ++++++-- .../IntegrationTestBase.cs | 82 +++++++++++++------ 3 files changed, 109 insertions(+), 39 deletions(-) diff --git a/src/AdminAssistant.Infra/DAL/EntityFramework/Model/AccountsSchema.cs b/src/AdminAssistant.Infra/DAL/EntityFramework/Model/AccountsSchema.cs index 08e6bb3e..bf8b6292 100644 --- a/src/AdminAssistant.Infra/DAL/EntityFramework/Model/AccountsSchema.cs +++ b/src/AdminAssistant.Infra/DAL/EntityFramework/Model/AccountsSchema.cs @@ -4,10 +4,33 @@ namespace AdminAssistant.Infra.DAL.EntityFramework.Model { - internal class AccountsSchema + public static class AccountsSchema { private const string Name = "Accounts"; + /// Sets up static lookup data for the accounts module. + /// + /// This is used both in the EF Core migrations for the prod DB as well as functional acceptance and integration + /// tests. In the latter case, it simulate migrations having been run, because the `Respawn` NuGet package + /// clears down the DB to blank between each test execution. + /// + /// `True` for EF Core migration code otherwise `False`. + /// Out of the box default bank account types. + public static BankAccountTypeEntity[] GetBankAccountTypesSeedData(bool includeIDs = false) + { + var currentAccount = new BankAccountTypeEntity() { Description = "Current Account", AllowCompany = true, AllowPersonal = true }; + if (includeIDs) currentAccount.BankAccountTypeID = 1; + + var savingsAccount = new BankAccountTypeEntity() { Description = "Savings Account", AllowCompany = true, AllowPersonal = true }; + if (includeIDs) savingsAccount.BankAccountTypeID = 2; + + return new BankAccountTypeEntity[] + { + currentAccount, + savingsAccount + }; + } + internal static void OnModelCreating(ModelBuilder builder) { AccountsSchema.Bank_OnModelCreating(builder); @@ -53,12 +76,8 @@ private static void BankAccountType_OnModelCreating(ModelBuilder builder) builder.Entity().Property(x => x.AllowCompany).IsRequired().HasDefaultValue(false); builder.Entity().Property(x => x.IsDeprecated).IsRequired().HasDefaultValue(false); - builder.Entity().HasData(new BankAccountTypeEntity[] - { - new BankAccountTypeEntity() { BankAccountTypeID = 1, Description = "Current Account", AllowCompany = true, AllowPersonal = true }, - new BankAccountTypeEntity() { BankAccountTypeID = 2, Description = "Savings Account", AllowCompany = true, AllowPersonal = true }, - }); - } + builder.Entity().HasData(GetBankAccountTypesSeedData(true)); + } private static void Payee_OnModelCreating(ModelBuilder builder) { diff --git a/src/AdminAssistant.Infra/DAL/EntityFramework/Model/CoreSchema.cs b/src/AdminAssistant.Infra/DAL/EntityFramework/Model/CoreSchema.cs index 1375fc46..0ee29846 100644 --- a/src/AdminAssistant.Infra/DAL/EntityFramework/Model/CoreSchema.cs +++ b/src/AdminAssistant.Infra/DAL/EntityFramework/Model/CoreSchema.cs @@ -4,10 +4,34 @@ namespace AdminAssistant.Infra.DAL.EntityFramework.Model.Core { - internal class CoreSchema + public static class CoreSchema { private const string Name = "Core"; + /// Sets up static lookup data for the core module. + /// + /// This is used both in the EF Core migrations for the prod DB as well as functional acceptance and integration + /// tests. In the latter case, it simulate migrations having been run, because the `Respawn` NuGet package + /// clears down the DB to blank between each test execution. + /// + /// `True` for EF Core migration code otherwise `False`. + /// Out of the box default currencies. + public static CurrencyEntity[] GetCurrencySeedData(bool includeIDs = false) + { + const string defaultDecimalFormat = "2.2-2"; + + var GBP = new CurrencyEntity { Symbol = "GBP", DecimalFormat = defaultDecimalFormat }; + if (includeIDs) GBP.CurrencyID = 1; + + var EUR = new CurrencyEntity { Symbol = "EUR", DecimalFormat = defaultDecimalFormat }; + if (includeIDs) EUR.CurrencyID = 2; + + var USD = new CurrencyEntity { Symbol = "USD", DecimalFormat = defaultDecimalFormat }; + if (includeIDs) USD.CurrencyID = 3; + + return new CurrencyEntity[] { GBP, EUR, USD }; + } + internal static void OnModelCreating(ModelBuilder modelBuilder) { CoreSchema.Audit_OnModelCreating(modelBuilder); @@ -76,12 +100,7 @@ private static void Currency_OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity().Property(x => x.Symbol).IsRequired().IsUnicode().HasMaxLength(Currency.SymbolMaxLength).HasColumnType($"CHAR({Currency.SymbolMaxLength})"); modelBuilder.Entity().Property(x => x.DecimalFormat).IsRequired().HasMaxLength(Currency.DecimalFormatMaxLength).HasColumnType($"CHAR({Currency.DecimalFormatMaxLength})"); modelBuilder.Entity().Property(x => x.IsDeprecated).IsRequired().HasDefaultValue(false); - modelBuilder.Entity().HasData(new CurrencyEntity[] - { - new CurrencyEntity { CurrencyID = 1, Symbol = "GBP", DecimalFormat = "2.2-2" }, - new CurrencyEntity { CurrencyID = 2, Symbol = "EUR", DecimalFormat = "2.2-2" }, - new CurrencyEntity { CurrencyID = 3, Symbol = "USD", DecimalFormat = "2.2-2" }, - }); + modelBuilder.Entity().HasData(GetCurrencySeedData(true)); } private static void Owner_OnModelCreating(ModelBuilder modelBuilder) diff --git a/src/AdminAssistant.Test/IntegrationTestBase.cs b/src/AdminAssistant.Test/IntegrationTestBase.cs index 134cfba5..d8efd4c5 100644 --- a/src/AdminAssistant.Test/IntegrationTestBase.cs +++ b/src/AdminAssistant.Test/IntegrationTestBase.cs @@ -1,10 +1,13 @@ #if DEBUG // quick and dirty fix for #85 category filtering breaking CI Unit Test run. using System; +using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Reflection; using System.Threading.Tasks; using AdminAssistant.Infra.DAL.EntityFramework; +using AdminAssistant.Infra.DAL.EntityFramework.Model; +using AdminAssistant.Infra.DAL.EntityFramework.Model.Accounts; using AdminAssistant.Infra.DAL.EntityFramework.Model.Core; using AdminAssistant.Infra.Providers; using AdminAssistant.UI.Shared.WebAPIClient.v1; @@ -69,9 +72,7 @@ public IntegrationTestBase() { "sysdiagrams", "__EFMigrationsHistory", - "tblObjectType", - "Currency", - "BankAccountType" + "tblObjectType" }, WithReseed = true }; @@ -90,17 +91,55 @@ protected async Task ResetDatabaseAsync() var dbContext = Container.GetRequiredService(); var dateTimeProvider = Container.GetRequiredService(); - // TODO: Replace this with IUserProfileRepository when it exists ... + var testBankAccountTypes = await SeedBankAccountTypeTestData(dbContext); + var testCurrencies = await SeedCurrencyTestData(dbContext); + var testUserProfile = await SeedUserProfileTestData(dbContext, dateTimeProvider).ConfigureAwait(false); + var testCompanyOwner = await SeedCompanyOwnerTestData(dbContext, dateTimeProvider, testUserProfile).ConfigureAwait(false); + var testPersonalOwner = await SeedPersonalOwnerTestData(dbContext, dateTimeProvider, testUserProfile).ConfigureAwait(false); + } + + protected virtual Action ConfigureTestServices() => services => + { + // Register the WebAPIClient using the test httpClient ... + services.AddTransient((sp) => + { + Guard.Against.Null(_httpClient.BaseAddress, "httpClient.BaseAddress"); + return new AdminAssistantWebAPIClient(_httpClient) { BaseUrl = _httpClient.BaseAddress.ToString() }; + }); + services.AddAutoMapper(typeof(MappingProfile)); + }; + + private async Task> SeedBankAccountTypeTestData(IApplicationDbContext dbContext) + { + var testBankAccountTypes = AccountsSchema.GetBankAccountTypesSeedData(); + dbContext.BankAccountTypes.AddRange(testBankAccountTypes); + await dbContext.SaveChangesAsync().ConfigureAwait(false); + return testBankAccountTypes.ToList(); + } + + private async Task> SeedCurrencyTestData(IApplicationDbContext dbContext) + { + var testCurrencies = CoreSchema.GetCurrencySeedData(); + dbContext.Currencies.AddRange(testCurrencies); + await dbContext.SaveChangesAsync().ConfigureAwait(false); + return testCurrencies.ToList(); + } + + private async Task SeedUserProfileTestData(IApplicationDbContext dbContext, IDateTimeProvider dateTimeProvider) + { var testUserProfile = new UserProfileEntity() { SignOn = "TestUser", - Audit = GetAuditForCreate() + Audit = GetAuditForCreate(dateTimeProvider) }; dbContext.UserProfiles.Add(testUserProfile); await dbContext.SaveChangesAsync().ConfigureAwait(false); + return testUserProfile; + } - // TODO: Replace this with ICompanyRepository when it exists ... + private async Task SeedCompanyOwnerTestData(IApplicationDbContext dbContext, IDateTimeProvider dateTimeProvider, UserProfileEntity testUserProfile) + { var testCompanyOwner = new OwnerEntity() { Company = new CompanyEntity() @@ -109,41 +148,34 @@ protected async Task ResetDatabaseAsync() CompanyNumber = "12345678910", VATNumber = "zz1224324543", DateOfIncorporation = DateTime.Today, - Audit = GetAuditForCreate(), + Audit = GetAuditForCreate(dateTimeProvider), UserProfile = testUserProfile } }; dbContext.Owners.Add(testCompanyOwner); await dbContext.SaveChangesAsync().ConfigureAwait(false); + return testCompanyOwner; + } - // TODO: Replace this with IPersonalDetailsRepository when it exists ... - var testPersonOwner = new OwnerEntity() + private async Task SeedPersonalOwnerTestData(IApplicationDbContext dbContext, IDateTimeProvider dateTimeProvider, UserProfileEntity testUserProfile) + { + var testPersonalOwner = new OwnerEntity() { PersonalDetails = new PersonalDetailsEntity() { - Audit = GetAuditForCreate(), + Audit = GetAuditForCreate(dateTimeProvider), UserProfile = testUserProfile } }; - dbContext.Owners.Add(testPersonOwner); + dbContext.Owners.Add(testPersonalOwner); await dbContext.SaveChangesAsync().ConfigureAwait(false); - - AuditEntity GetAuditForCreate() => new() { CreatedBy = "TestUser", CreatedOn = dateTimeProvider.UtcNow }; + return testPersonalOwner; } - protected virtual Action ConfigureTestServices() => services => - { - // Register the WebAPIClient using the test httpClient ... - services.AddTransient((sp) => - { - Guard.Against.Null(_httpClient.BaseAddress, "httpClient.BaseAddress"); - return new AdminAssistantWebAPIClient(_httpClient) { BaseUrl = _httpClient.BaseAddress.ToString() }; - }); - services.AddAutoMapper(typeof(MappingProfile)); - }; - + private AuditEntity GetAuditForCreate(IDateTimeProvider dateTimeProvider) + => new() { CreatedBy = "TestUser", CreatedOn = dateTimeProvider.UtcNow }; -#region IDisposable + #region IDisposable private bool disposedValue;