-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
138 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
using System.Collections; | ||
using CleanAspCore.Data; | ||
using Microsoft.EntityFrameworkCore; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Logging; | ||
using Testcontainers.PostgreSql; | ||
|
||
namespace CleanAspCore.Api.Tests.Data; | ||
|
||
[FixtureLifeCycle(LifeCycle.SingleInstance)] | ||
[Parallelizable(ParallelScope.Self)] | ||
public class MigrationTests | ||
{ | ||
#pragma warning disable NUnit1032 | ||
private PostgreSqlContainer _databaseContainer = null!; | ||
#pragma warning restore NUnit1032 | ||
private ILogger<MigrationTests> _logger = null!; | ||
private AsyncServiceScope _scope; | ||
|
||
[SetUp] | ||
public void BeforeTestCase() | ||
{ | ||
_scope = GlobalSetup.Provider.CreateAsyncScope(); | ||
_databaseContainer = _scope.ServiceProvider.GetRequiredService<PostgreSqlContainer>(); | ||
_logger = _scope.ServiceProvider.GetRequiredService<ILogger<MigrationTests>>(); | ||
} | ||
|
||
[TearDown] | ||
public async Task AfterTestCase() | ||
{ | ||
await _scope.DisposeAsync(); | ||
} | ||
|
||
[TestCaseSource(typeof(MigrationTestCases))] | ||
public async Task MigrationsUpAndDown_NoErrors2(MigrationScript migration) | ||
{ | ||
var databaseName = "migrationstest"; | ||
var createDatabaseResult = await _databaseContainer.CreateDatabase(databaseName); | ||
createDatabaseResult.ExitCode.Should().Be(0, $"Error while creating database for migrations: {createDatabaseResult.Stderr}"); | ||
|
||
var migrator = new PostgreSqlMigrator(_databaseContainer, _logger, databaseName); | ||
var upResult = await migrator.Up(migration); | ||
upResult.ExitCode.Should().Be(0, $"Error during migration up: {upResult.Stderr}"); | ||
var downResult = await migrator.Down(migration); | ||
downResult.ExitCode.Should().Be(0, $"Error during migration down: {downResult.Stderr}"); | ||
var upResult2 = await migrator.Up(migration); | ||
upResult.ExitCode.Should().Be(0, $"Error during migration up2: {upResult2.Stderr}"); | ||
} | ||
|
||
private sealed class MigrationTestCases : IEnumerable | ||
Check warning on line 50 in CleanAspCore.Api.Tests/Data/MigrationTests.cs GitHub Actions / build
Check warning on line 50 in CleanAspCore.Api.Tests/Data/MigrationTests.cs GitHub Actions / build
|
||
{ | ||
public IEnumerator GetEnumerator() | ||
{ | ||
using DbContext context = new HrContext(); | ||
var migrations = context.GenerateMigrationScripts(); | ||
|
||
foreach (var migration in migrations) | ||
{ | ||
yield return new TestCaseData(migration); | ||
} | ||
} | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
CleanAspCore.Api.Tests/TestSetup/PostgreSqlDatabaseExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
using System.Text; | ||
using DotNet.Testcontainers.Configurations; | ||
using DotNet.Testcontainers.Containers; | ||
using Testcontainers.PostgreSql; | ||
|
||
namespace CleanAspCore.Api.Tests.TestSetup; | ||
|
||
public static class PostgreSqlDatabaseExtensions | ||
{ | ||
public static async Task<ExecResult> ExecScriptAsync(this PostgreSqlContainer container, string scriptContent, string database, CancellationToken ct = default) | ||
{ | ||
var scriptFilePath = string.Join("/", string.Empty, "tmp", Guid.NewGuid().ToString("D"), Path.GetRandomFileName()); | ||
|
||
await container.CopyAsync(Encoding.Default.GetBytes(scriptContent), scriptFilePath, Unix.FileMode644, ct) | ||
.ConfigureAwait(false); | ||
|
||
var connectionString = ParseConnectionString(container.GetConnectionString()); | ||
|
||
return await container.ExecAsync(new[] { "psql", "--username", connectionString.UserId, "--dbname", database, "--file", scriptFilePath }, ct) | ||
.ConfigureAwait(false); | ||
} | ||
|
||
private sealed record SqlContainerConnectionString(string UserId, string Password); | ||
private static SqlContainerConnectionString ParseConnectionString(string connectionString) | ||
{ | ||
var dic = connectionString | ||
.Split(';') | ||
.Select(x => x.Split('=')) | ||
.ToDictionary(x => x[0], x => x[1]); | ||
|
||
return new SqlContainerConnectionString(dic["Username"], dic["Password"]); | ||
} | ||
|
||
public static async Task<ExecResult> CreateDatabase(this PostgreSqlContainer container, string name) | ||
{ | ||
return await container.ExecScriptAsync($"CREATE DATABASE {name};"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
using DotNet.Testcontainers.Containers; | ||
using Microsoft.Extensions.Logging; | ||
using Testcontainers.PostgreSql; | ||
|
||
namespace CleanAspCore.Api.Tests.TestSetup; | ||
|
||
public class PostgreSqlMigrator | ||
{ | ||
private readonly PostgreSqlContainer _container; | ||
private readonly ILogger _logger; | ||
private readonly string _databaseName; | ||
|
||
public PostgreSqlMigrator(PostgreSqlContainer container, ILogger logger, string databaseName) | ||
{ | ||
_container = container; | ||
_logger = logger; | ||
_databaseName = databaseName; | ||
} | ||
|
||
public async Task<ExecResult> Up(MigrationScript script) => await ExecuteMigration(script.UpScript, script.FromMigration, script.ToMigration); | ||
|
||
public async Task<ExecResult> Down(MigrationScript script) => await ExecuteMigration(script.DownScript, script.ToMigration, script.FromMigration); | ||
|
||
private async Task<ExecResult> ExecuteMigration(string script, string from, string to) | ||
{ | ||
_logger.LogInformation("Migrating from {FromMigration} to {ToMigration}", from, to); | ||
Check warning on line 26 in CleanAspCore.Api.Tests/TestSetup/PostgreSqlMigrator.cs GitHub Actions / build
Check warning on line 26 in CleanAspCore.Api.Tests/TestSetup/PostgreSqlMigrator.cs GitHub Actions / build
|
||
try | ||
{ | ||
// This will use sqlcmd to execute the script. The behavior is different from executing the migration from code, for instance the script will fail if a column is referenced that does not exist. | ||
return await _container.ExecScriptAsync(script, _databaseName); | ||
} | ||
catch (Exception e) | ||
{ | ||
throw new InvalidOperationException($"Error while migrating from {to} to {from}", e); | ||
} | ||
} | ||
} |