Skip to content

Commit

Permalink
add migration test
Browse files Browse the repository at this point in the history
  • Loading branch information
Barsonax committed Apr 28, 2024
1 parent 3b2f276 commit 18126d7
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 0 deletions.
63 changes: 63 additions & 0 deletions CleanAspCore.Api.Tests/Data/MigrationTests.cs
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

View workflow job for this annotation

GitHub Actions / build

'MigrationTests.MigrationTestCases' is an internal class that is apparently never instantiated. If so, remove the code from the assembly. If this class is intended to contain only static members, make it 'static' (Module in Visual Basic). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1812)

Check warning on line 50 in CleanAspCore.Api.Tests/Data/MigrationTests.cs

View workflow job for this annotation

GitHub Actions / build

'MigrationTests.MigrationTestCases' is an internal class that is apparently never instantiated. If so, remove the code from the assembly. If this class is intended to contain only static members, make it 'static' (Module in Visual Basic). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1812)
{
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 CleanAspCore.Api.Tests/TestSetup/PostgreSqlDatabaseExtensions.cs
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};");
}
}
37 changes: 37 additions & 0 deletions CleanAspCore.Api.Tests/TestSetup/PostgreSqlMigrator.cs
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

View workflow job for this annotation

GitHub Actions / build

For improved performance, use the LoggerMessage delegates instead of calling 'LoggerExtensions.LogInformation(ILogger, string?, params object?[])' (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1848)

Check warning on line 26 in CleanAspCore.Api.Tests/TestSetup/PostgreSqlMigrator.cs

View workflow job for this annotation

GitHub Actions / build

For improved performance, use the LoggerMessage delegates instead of calling 'LoggerExtensions.LogInformation(ILogger, string?, params object?[])' (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1848)
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);
}
}
}

0 comments on commit 18126d7

Please sign in to comment.