Skip to content

Commit

Permalink
Rework DropDatabaseOnRemove for Integresql v1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Shaddix committed Mar 6, 2024
1 parent b81f7cf commit 0d40001
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public interface IDatabaseInitializer : IDisposable, IUseProvider
/// <returns>Connection string to a copy of template database</returns>
Task<string> CreateDatabaseGetConnectionString<TDbContext>(
DatabaseSeedingOptions<TDbContext> databaseSeeding = null
) where TDbContext : DbContext;
)
where TDbContext : DbContext;

/// <summary>
/// Creates a template database (if not created before) using DbContext.Database.EnsureCreated and
Expand All @@ -28,15 +29,17 @@ Task<string> CreateDatabaseGetConnectionString<TDbContext>(
/// <returns>Connection string to a copy of template database</returns>
string CreateDatabaseGetConnectionStringSync<TDbContext>(
DatabaseSeedingOptions<TDbContext> databaseSeeding = null
) where TDbContext : DbContext;
)
where TDbContext : DbContext;

/// <summary>
/// Creates the DbContextOptionsBuilder for passed connection string.
/// Should be stored in a Test Class field and used to create a DbContext instance (pointing to the same DB during the test).
/// </summary>
DbContextOptionsBuilder<TDbContext> CreateDbContextOptionsBuilder<TDbContext>(
string connectionString
) where TDbContext : DbContext;
)
where TDbContext : DbContext;

/// <summary>
/// Creates the database using <see cref="CreateDatabaseGetConnectionString{TDbContext}"/>
Expand All @@ -45,7 +48,8 @@ string connectionString
/// </summary>
Task<DbContextOptionsBuilder<TDbContext>> CreateDatabaseGetDbContextOptionsBuilder<TDbContext>(
DatabaseSeedingOptions<TDbContext> seedingOptions = null
) where TDbContext : DbContext;
)
where TDbContext : DbContext;

/// <summary>
/// Creates the database using <see cref="CreateDatabaseGetConnectionString{TDbContext}"/>
Expand All @@ -54,10 +58,12 @@ Task<DbContextOptionsBuilder<TDbContext>> CreateDatabaseGetDbContextOptionsBuild
/// </summary>
DbContextOptionsBuilder<TDbContext> CreateDatabaseGetDbContextOptionsBuilderSync<TDbContext>(
DatabaseSeedingOptions<TDbContext> seedingOptions = null
) where TDbContext : DbContext;
)
where TDbContext : DbContext;

/// <summary>
/// Returns test database to a pool.
/// You need to cleanup the data in that database yourself!
/// </summary>
Task ReturnDatabaseToPool(string connectionString);

Expand Down Expand Up @@ -85,5 +91,6 @@ DbContextOptionsBuilder<TDbContext> CreateDatabaseGetDbContextOptionsBuilderSync
void UseProvider<TDbContext>(
DbContextOptionsBuilder options,
DatabaseSeedingOptions<TDbContext> databaseSeedingOptions
) where TDbContext : DbContext;
)
where TDbContext : DbContext;
}
47 changes: 45 additions & 2 deletions src/MccSoft.IntegreSql.EF/IntegreSqlClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ public async Task<CreateTemplateDto> InitializeTemplate(string hash)

if (response.IsSuccessStatusCode)
{
return await response.Content
.ReadFromJsonAsync<CreateTemplateDto>()
return await response
.Content.ReadFromJsonAsync<CreateTemplateDto>()
.ConfigureAwait(false);
}

Expand Down Expand Up @@ -177,6 +177,49 @@ public async Task<GetDatabaseDto> GetTestDatabase(string hash)
throw new NotImplementedException("We should never reach this point");
}

/// <summary>
/// Returns test database to a pool (which allows consequent tests to reuse this database).
/// This method (contrary to <see cref="ReturnTestDatabase"/>) will tell IntegreSQL to cleanup the database.
/// </summary>
public async Task ReleaseTestDatabase(string hash, int id)
{
HttpResponseMessage response;
try
{
response = await _httpClient
.PostAsync($"templates/{hash}/tests/{id}/recreate", null)
.ConfigureAwait(false);
}
catch (HttpRequestException e)
{
throw new IntegreSqlNotRunningException(_httpClient.BaseAddress?.ToString(), e);
}

if (response.IsSuccessStatusCode)
return;

if (response.StatusCode == HttpStatusCode.NotFound)
{
throw new IntegreSqlTemplateNotFoundException(hash);
}
if (response.StatusCode == HttpStatusCode.ServiceUnavailable)
{
throw new IntegreSqlPostgresNotAvailableException(_httpClient.BaseAddress?.ToString());
}

response.EnsureSuccessStatusCode();
throw new NotImplementedException("We should never reach this point");
}

/// <summary>
/// Returns test database to a pool (which allows consequent tests to reuse this database).
/// Note that you need to clean up database by yourself before returning it to the pool!
/// If you return a dirty database, consequent tests might fail!
/// If you don't want to clean it up, just don't use this function.
/// Dirty databases are automatically deleted by IntegreSQL once database number exceeds a certain limit (500 by default).
///
/// Consider using <see cref="ReleaseTestDatabase"/> if you want the DB to be deleted/recreated by IntegreSQL
/// </summary>
public async Task ReturnTestDatabase(string hash, int id)
{
HttpResponseMessage response;
Expand Down
2 changes: 1 addition & 1 deletion src/MccSoft.IntegreSql.EF/MccSoft.IntegreSql.EF.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Version>0.8.16</Version>
<Version>0.10.1</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>MCC Soft</Authors>
<Description>OpenIddict extension to support Auth code flow fo built-in ASP.Net identity providers</Description>
Expand Down
21 changes: 8 additions & 13 deletions src/MccSoft.IntegreSql.EF/NpgsqlDatabaseInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,9 @@ public class NpgsqlDatabaseInitializer : BaseDatabaseInitializer
public static bool UseMd5Hash = true;

/// <summary>
/// Drop database at the end of each test (when true) or not.
/// Release database (call IntegreSQL /api/v1/templates/:hash/tests/:id/recreate) at the end of each test (when true) or not.
///
/// Old databases will be reused by IntegreSQL automatically.
/// Previously we were removing databases by default, which actually interfere with IntegreSQL.
/// It was 'hanging' when tried to reuse the removed databases.
/// However, it was reported that if not dropped, Postgres starts to consume a lot of RAM.
/// So one might be willing to drop anyway
/// (though, for the latter case I'd recommend reducing the `INTEGRESQL_TEST_MAX_POOL_SIZE` in docker).
/// This API is available starting from IntegreSQL v1.1
/// </summary>
public bool DropDatabaseOnRemove { get; set; }

Expand Down Expand Up @@ -195,12 +190,12 @@ public override async Task RemoveDatabase(string connectionString)
{
if (DropDatabaseOnRemove)
{
await using var connection = new NpgsqlConnection(connectionString);
string database = connection.Database;
connection.Open();
connection.ChangeDatabase("postgres");
var command = new NpgsqlCommand($"DROP DATABASE \"{database}\"", connection);
command.ExecuteNonQuery();
var connectionStringInfo = ConnectionStringInfos[connectionString];

await _integreSqlClient.ReturnTestDatabase(
connectionStringInfo.Hash,
connectionStringInfo.Id
);
}
else
{
Expand Down
53 changes: 26 additions & 27 deletions tests/ExampleWeb.IntegrationTests/IntegrationTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,32 +24,28 @@ protected IntegrationTestBase(DatabaseType databaseType)
new DatabaseSeedingOptions<ExampleDbContext>(Name: "Integration")
);

var webAppFactory = new WebApplicationFactory<Program>().WithWebHostBuilder(
builder =>
var webAppFactory = new WebApplicationFactory<Program>().WithWebHostBuilder(builder =>
{
builder.ConfigureAppConfiguration(
(context, configuration) =>
{
configuration.AddInMemoryCollection(
new KeyValuePair<string, string>[] { new("DisableSeed", "true") }
);
}
);
builder.ConfigureServices(services =>
{
builder.ConfigureAppConfiguration(
(context, configuration) =>
{
configuration.AddInMemoryCollection(
new KeyValuePair<string, string>[] { new("DisableSeed", "true") }
);
}
var descriptor = services.Single(d =>
d.ServiceType == typeof(DbContextOptions<ExampleDbContext>)
);
builder.ConfigureServices(
services =>
{
var descriptor = services.Single(
d => d.ServiceType == typeof(DbContextOptions<ExampleDbContext>)
);
services.Remove(descriptor);
services.Remove(descriptor);

services.AddDbContext<ExampleDbContext>(
options => _databaseInitializer.UseProvider(options, _connectionString)
);
}
services.AddDbContext<ExampleDbContext>(options =>
_databaseInitializer.UseProvider(options, _connectionString)
);
}
);
});
});

_httpClient = webAppFactory.CreateDefaultClient();
}
Expand All @@ -59,11 +55,14 @@ private IDatabaseInitializer CreateDatabaseInitializer(DatabaseType databaseType
return databaseType switch
{
DatabaseType.Postgres
=> new NpgsqlDatabaseInitializer(
// This is needed if you run tests NOT inside the container.
// 5434 is the public port number of Postgresql instance
connectionStringOverride: new() { Host = "localhost", Port = 5434, }
),
=> new NpgsqlDatabaseInitializer(
// This is needed if you run tests NOT inside the container.
// 5434 is the public port number of Postgresql instance
connectionStringOverride: new() { Host = "localhost", Port = 5434, }
)
{
DropDatabaseOnRemove = true,
},
DatabaseType.Sqlite => new SqliteDatabaseInitializer(),
_ => throw new ArgumentOutOfRangeException(nameof(databaseType), databaseType, null)
};
Expand Down
13 changes: 8 additions & 5 deletions tests/ExampleWeb.UnitTests/UnitTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,14 @@ public UnitTestBase(
{
null => null,
DatabaseType.Postgres
=> new NpgsqlDatabaseInitializer(
// This is needed if you run tests NOT inside the container.
// 5434 is the public port number of Postgresql instance
connectionStringOverride: new() { Host = "localhost", Port = 5434 }
),
=> new NpgsqlDatabaseInitializer(
// This is needed if you run tests NOT inside the container.
// 5434 is the public port number of Postgresql instance
connectionStringOverride: new() { Host = "localhost", Port = 5434 }
)
{
DropDatabaseOnRemove = true,
},
DatabaseType.Sqlite => new SqliteDatabaseInitializer(),
_ => throw new ArgumentOutOfRangeException(nameof(databaseType), databaseType, null)
};
Expand Down

0 comments on commit 0d40001

Please sign in to comment.