Skip to content

Commit

Permalink
Remove in-memory DB support. (#183)
Browse files Browse the repository at this point in the history
* Don't use in-memory DB.

* Remove in-memory DB options from configs.

* Replace EF Core in-mem depend with EF Core depend.

* Remove in-mem DB env vars from .env files.

* Use default DB vars for swagger-gen.env.

* Remove mention of in-mem DB from README.md.
  • Loading branch information
TheTedder authored Aug 16, 2023
1 parent d5d8b72 commit 70a6c9e
Show file tree
Hide file tree
Showing 9 changed files with 28 additions and 116 deletions.
5 changes: 0 additions & 5 deletions LeaderboardBackend.Test/Fixtures/PostgresDatabaseFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,6 @@ internal class PostgresDatabaseFixture
[OneTimeSetUp]
public static async Task OneTimeSetup()
{
if (TestConfig.DatabaseBackend != DatabaseBackend.TestContainer)
{
return;
}

PostgresContainer = new PostgreSqlBuilder()
.WithTmpfsMount("/var/lib/postgresql/data") // db files in-memory
.Build();
Expand Down
15 changes: 0 additions & 15 deletions LeaderboardBackend.Test/Fixtures/TestConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,8 @@ namespace LeaderboardBackend.Test.Fixtures;

internal static class TestConfig
{
public static DatabaseBackend DatabaseBackend { get; private set; } =
DatabaseBackend.TestContainer;

static TestConfig()
{
string? backendVar = Environment.GetEnvironmentVariable("INTEGRATION_TESTS_DB_BACKEND");
if (Enum.TryParse(backendVar, out DatabaseBackend dbBackend))
{
DatabaseBackend = dbBackend;
}

Bogus.Randomizer.Seed = new Random(43817269); // fixed seed for repeatable tests
}
}

internal enum DatabaseBackend
{
InMemory,
TestContainer,
}
82 changes: 17 additions & 65 deletions LeaderboardBackend.Test/TestApi/TestApiFactory.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Net.Http;
using LeaderboardBackend.Models.Entities;
using LeaderboardBackend.Test.Fixtures;
using LeaderboardBackend.Test.Lib;
using MailKit.Net.Smtp;
using Microsoft.AspNetCore.Hosting;
Expand All @@ -25,33 +24,22 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)

builder.ConfigureServices(services =>
{
if (TestConfig.DatabaseBackend == DatabaseBackend.TestContainer)
if (PostgresDatabaseFixture.PostgresContainer is null)
{
if (PostgresDatabaseFixture.PostgresContainer is null)
{
throw new InvalidOperationException("Postgres container is not initialized.");
}
services.Configure<ApplicationContextConfig>(conf =>
{
conf.UseInMemoryDb = false;
conf.Pg = new PostgresConfig
{
Db = PostgresDatabaseFixture.Database!,
Port = (ushort)PostgresDatabaseFixture.Port,
Host = PostgresDatabaseFixture.PostgresContainer.Hostname,
User = PostgresDatabaseFixture.Username!,
Password = PostgresDatabaseFixture.Password!
};
});
throw new InvalidOperationException("Postgres container is not initialized.");
}
else
services.Configure<ApplicationContextConfig>(conf =>
{
services.Configure<ApplicationContextConfig>(conf =>
conf.Pg = new PostgresConfig
{
conf.UseInMemoryDb = true;
});
}
Db = PostgresDatabaseFixture.Database!,
Port = (ushort)PostgresDatabaseFixture.Port,
Host = PostgresDatabaseFixture.PostgresContainer.Hostname,
User = PostgresDatabaseFixture.Username!,
Password = PostgresDatabaseFixture.Password!
};
});
// mock SMTP client
services.Replace(ServiceDescriptor.Transient<ISmtpClient>(_ => new Mock<ISmtpClient>().Object));
Expand All @@ -78,26 +66,11 @@ public void InitializeDatabase()

private static void InitializeDatabase(ApplicationContext dbContext)
{
switch (TestConfig.DatabaseBackend)
if (!PostgresDatabaseFixture.HasCreatedTemplate)
{
case DatabaseBackend.TestContainer:
if (!PostgresDatabaseFixture.HasCreatedTemplate)
{
dbContext.MigrateDatabase();
Seed(dbContext);
PostgresDatabaseFixture.CreateTemplateFromCurrentDb();
}

break;
case DatabaseBackend.InMemory:
if (dbContext.Database.EnsureCreated())
{
Seed(dbContext);
}

break;
default:
throw new NotImplementedException("Database initialization is not implemented");
dbContext.MigrateDatabase();
Seed(dbContext);
PostgresDatabaseFixture.CreateTemplateFromCurrentDb();
}
}

Expand Down Expand Up @@ -127,27 +100,6 @@ private static void Seed(ApplicationContext dbContext)
/// </summary>
public void ResetDatabase()
{
switch (TestConfig.DatabaseBackend)
{
case DatabaseBackend.InMemory:
ResetInMemoryDb();
break;
case DatabaseBackend.TestContainer:
PostgresDatabaseFixture.ResetDatabaseToTemplate();
break;
default:
throw new NotImplementedException("Database reset is not implemented.");
}
}

private void ResetInMemoryDb()
{
using IServiceScope scope = Services.CreateScope();
ApplicationContext dbContext =
scope.ServiceProvider.GetRequiredService<ApplicationContext>();
dbContext.Database.EnsureDeleted();
dbContext.Database.EnsureCreated();

Seed(dbContext);
PostgresDatabaseFixture.ResetDatabaseToTemplate();
}
}
2 changes: 1 addition & 1 deletion LeaderboardBackend/LeaderboardBackend.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@
<PackageReference Include="FluentValidation.AspNetCore" Version="11.2.2" />
<PackageReference Include="MailKit" Version="4.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.9">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.9" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.2" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.6" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@ public class ApplicationContextConfig : IValidatableObject
public const string KEY = "ApplicationContext";

public bool MigrateDb { get; set; } = false;
public bool UseInMemoryDb { get; set; } = false;
public PostgresConfig? Pg { get; set; }

public IEnumerable<ValidationResult> Validate(ValidationContext context)
{
if (!UseInMemoryDb && Pg == null)
if (Pg == null)
{
yield return new ValidationResult(
"Missing database configuration.",
new[] { nameof(UseInMemoryDb), nameof(Pg) }
new[] { nameof(Pg) }
);
}
}
Expand Down
18 changes: 3 additions & 15 deletions LeaderboardBackend/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,7 @@
ApplicationContextConfig appConfig = services
.GetRequiredService<IOptions<ApplicationContextConfig>>()
.Value;
if (appConfig.UseInMemoryDb)
{
opt.UseInMemoryDatabase("LeaderboardBackend");
}
else if (appConfig.Pg is not null)
if (appConfig.Pg is not null)
{
PostgresConfig db = appConfig.Pg;
NpgsqlConnectionStringBuilder connectionBuilder =
Expand Down Expand Up @@ -302,19 +298,11 @@

if (args.Contains("--migrate-db")) // the only way to migrate a production database
{
if (!config.UseInMemoryDb)
{
context.Database.Migrate();
}

context.Database.Migrate();
return;
}

if (config.UseInMemoryDb)
{
context.Database.EnsureCreated();
}
else if (config.MigrateDb && app.Environment.IsDevelopment())
if (config.MigrateDb && app.Environment.IsDevelopment())
{
// migration as part of the startup phase (dev env only)
context.MigrateDatabase();
Expand Down
9 changes: 0 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,6 @@ cp example.env .env
```

### Running the Database(s)
We use Postgres, but have the ability to run Entity Framework's in-memory DB as well instead, which allows quicker debugging.

**Running with Postgres is highly encouraged**, as the in-memory DB will not behave the same as Postgres,
and [some features will not work at all](https://learn.microsoft.com/en-us/ef/core/testing/choosing-a-testing-strategy#in-memory-as-a-database-fake) (for example: transactions and constraints).

#### Postgres with Docker compose
As mentioned above, we run Docker containers for the DB. After [installing Docker Compose](https://docs.docker.com/compose/install/), run this command in the project root:
Expand All @@ -101,9 +97,6 @@ If you're using the default values provided in `example.env`, input these values
| Password | `example` | Corresponds to `ApplicationContext__PG__PASSWORD`
| Database | `leaderboardsmain` | Corresponds to `ApplicationContext__PG__DB`

#### In-memory database
To use the in-memory database, set `ApplicationContext__UseInMemoryDb` in your `.env` file to `true`.

### Visual Studio

#### First Time Setup
Expand Down Expand Up @@ -158,8 +151,6 @@ dotnet run # or `dotnet watch` to run with hot reload

#### Test the App
Docker is required to run integration tests against a real Postgres database.
However, if you would still like to run the tests with an in-memory database, you can set the following environment variable: `INTEGRATION_TESTS_DB_BACKEND=InMemory`.

To run the tests, run the following commands from the root of the repository:

```bash
Expand Down
2 changes: 0 additions & 2 deletions example.env
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

AllowedOrigins=*

ApplicationContext__UseInMemoryDb=false

# migrate the database on startup (Development only)
ApplicationContext__MigrateDb=false

Expand Down
6 changes: 5 additions & 1 deletion swagger-gen.env
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# environment used for the openapi.json generation build step (see .csproj)

ApplicationContext__UseInMemoryDb=true
JWT__KEY=coolsecretkeywow
JWT__ISSUER=leaderboards.gg

ApplicationContext__PG__HOST=localhost
ApplicationContext__PG__USER=admin
ApplicationContext__PG__PASSWORD=example
ApplicationContext__PG__DB=leaderboardsmain

0 comments on commit 70a6c9e

Please sign in to comment.