diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 0468edb8f..f5344ccf4 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -131,7 +131,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: temurin - java-version: 11 + java-version: 17 - name: Setup .NET uses: actions/setup-dotnet@v3 diff --git a/build.cake b/build.cake index 9f4dad753..101270b7a 100644 --- a/build.cake +++ b/build.cake @@ -1,4 +1,4 @@ -#tool nuget:?package=dotnet-sonarscanner&version=5.13.0 +#tool nuget:?package=dotnet-sonarscanner&version=5.13.1 #addin nuget:?package=Cake.Sonar&version=1.1.31 diff --git a/examples/Flyway/.editorconfig b/examples/Flyway/.editorconfig new file mode 100644 index 000000000..6f066619d --- /dev/null +++ b/examples/Flyway/.editorconfig @@ -0,0 +1 @@ +root = true \ No newline at end of file diff --git a/examples/Flyway/.gitattributes b/examples/Flyway/.gitattributes new file mode 100644 index 000000000..212566614 --- /dev/null +++ b/examples/Flyway/.gitattributes @@ -0,0 +1 @@ +* text=auto \ No newline at end of file diff --git a/examples/Flyway/Directory.Build.props b/examples/Flyway/Directory.Build.props new file mode 100644 index 000000000..b28d9e005 --- /dev/null +++ b/examples/Flyway/Directory.Build.props @@ -0,0 +1,28 @@ + + + + $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), Flyway.sln))/ + + + 0.1.0 + $(AssemblyName) + $(Version) + $(Version) + $(Version) + en-US + Andre Hofmeister + + + + + + + + git + https://github.com/testcontainers/testcontainers-dotnet-sample + + + 10.0 + enable + + \ No newline at end of file diff --git a/examples/Flyway/Flyway.sln b/examples/Flyway/Flyway.sln new file mode 100644 index 000000000..8f623edd8 --- /dev/null +++ b/examples/Flyway/Flyway.sln @@ -0,0 +1,26 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{424CFC36-D6F7-4DBB-BD1C-0C84FE30E665}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flyway.Tests", "tests\Flyway.Tests\Flyway.Tests.csproj", "{1E882BFE-4A5E-4E58-A533-81E1BE9E9BFD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1E882BFE-4A5E-4E58-A533-81E1BE9E9BFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1E882BFE-4A5E-4E58-A533-81E1BE9E9BFD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E882BFE-4A5E-4E58-A533-81E1BE9E9BFD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1E882BFE-4A5E-4E58-A533-81E1BE9E9BFD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1E882BFE-4A5E-4E58-A533-81E1BE9E9BFD} = {424CFC36-D6F7-4DBB-BD1C-0C84FE30E665} + EndGlobalSection +EndGlobal diff --git a/examples/Flyway/Packages.props b/examples/Flyway/Packages.props new file mode 100644 index 000000000..adfd5db4a --- /dev/null +++ b/examples/Flyway/Packages.props @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Flyway/README.md b/examples/Flyway/README.md new file mode 100644 index 000000000..dbf2e9b29 --- /dev/null +++ b/examples/Flyway/README.md @@ -0,0 +1,3 @@ +# Testcontainers for .NET Flyway example + +This example demonstrates how to use Testcontainers in conjunction with Flyway to execute database migrations and prepare a dependent database before running tests. The `FlywayTest` test class executes its tests against the pre-configured PostgreSQL database, interacting with the table that is created, altered, and seeded beforehand. This test class receives a class fixture, which provides access to the prepared database through the `DbConnection` property. The database is started, created, and seeded once and is shared across the tests within the `FlywayTest` test collection. \ No newline at end of file diff --git a/examples/Flyway/src/.gitkeep b/examples/Flyway/src/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/examples/Flyway/tests/Flyway.Tests/DbFixture.cs b/examples/Flyway/tests/Flyway.Tests/DbFixture.cs new file mode 100644 index 000000000..7359c0350 --- /dev/null +++ b/examples/Flyway/tests/Flyway.Tests/DbFixture.cs @@ -0,0 +1,74 @@ +namespace Flyway.Tests; + +[UsedImplicitly] +public sealed class DbFixture : IAsyncLifetime +{ + private readonly INetwork _network = new NetworkBuilder().Build(); + + private readonly IContainer _postgreSqlContainer; + + private readonly IContainer _flywayContainer; + + public DbFixture() + { + // Testcontainers starts the dependent database (PostgreSQL) and the database + // migration tool Flyway. It establishes a network connection between these two + // containers. Before starting the Flyway container, Testcontainers copies the SQL + // migration files into it. When the Flyway container starts, it initiates the + // dependent database container, connects to it, and begins the database migration + // as soon as the database is ready. Once the migration is finished, the Flyway + // container exits, and the database container becomes available for tests. + + _postgreSqlContainer = new PostgreSqlBuilder() + .WithImage("postgres:15-alpine") + .WithNetwork(_network) + .WithNetworkAliases(nameof(_postgreSqlContainer)) + .Build(); + + // The member `WithResourceMapping(string, string)` copies the SQL migration files + // from the test host into the Flyway container before it starts. This ensures that + // the files are available as soon as the container starts. Flyway will + // automatically pick them up and start the database migration process. + + _flywayContainer = new ContainerBuilder() + .WithImage("flyway/flyway:9-alpine") + .WithResourceMapping("migrate/", "/flyway/sql/") + .WithCommand("-url=jdbc:postgresql://" + nameof(_postgreSqlContainer) + "/") + .WithCommand("-user=" + PostgreSqlBuilder.DefaultUsername) + .WithCommand("-password=" + PostgreSqlBuilder.DefaultPassword) + .WithCommand("-connectRetries=3") + .WithCommand("migrate") + .WithNetwork(_network) + .DependsOn(_postgreSqlContainer) + .WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(new MigrationCompleted())) + .Build(); + } + + public DbConnection DbConnection => new NpgsqlConnection(((PostgreSqlContainer)_postgreSqlContainer).GetConnectionString()); + + public Task InitializeAsync() + { + return _flywayContainer.StartAsync(); + } + + public Task DisposeAsync() + { + // We do not need to manually dispose Docker resources. If resources depend on each + // other, it is necessary to dispose them in the correct order. Testcontainers' + // Resource Reaper (Ryuk) will reliably take care of these resources and dispose + // them after the test automatically. + return Task.CompletedTask; + } + + private sealed class MigrationCompleted : IWaitUntil + { + // The Flyway container will exit after executing the database migration. We do not + // check if the migration was successful. To verify its success, we can either + // check the exit code of the container or the console output, respectively the + // standard output (stdout) or error output (stderr). + public Task UntilAsync(IContainer container) + { + return Task.FromResult(TestcontainersStates.Exited.Equals(container.State)); + } + } +} \ No newline at end of file diff --git a/examples/Flyway/tests/Flyway.Tests/Flyway.Tests.csproj b/examples/Flyway/tests/Flyway.Tests/Flyway.Tests.csproj new file mode 100644 index 000000000..fbd9cd2b5 --- /dev/null +++ b/examples/Flyway/tests/Flyway.Tests/Flyway.Tests.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Flyway/tests/Flyway.Tests/FlywayTest.cs b/examples/Flyway/tests/Flyway.Tests/FlywayTest.cs new file mode 100644 index 000000000..5aa7804bb --- /dev/null +++ b/examples/Flyway/tests/Flyway.Tests/FlywayTest.cs @@ -0,0 +1,34 @@ +namespace Flyway.Tests; + +public sealed class FlywayTest : IClassFixture, IDisposable +{ + private readonly DbConnection _dbConnection; + + public FlywayTest(DbFixture db) + { + _dbConnection = db.DbConnection; + _dbConnection.Open(); + } + + public void Dispose() + { + _dbConnection.Dispose(); + } + + [Fact] + public void UsersTableContainsJohnDoe() + { + // Given + using var command = _dbConnection.CreateCommand(); + command.CommandText = "SELECT username, email, age FROM users;"; + + // When + using var dataReader = command.ExecuteReader(); + + // Then + Assert.True(dataReader.Read()); + Assert.Equal("john_doe", dataReader.GetString(0)); + Assert.Equal("john@example.com", dataReader.GetString(1)); + Assert.Equal(30, dataReader.GetInt32(2)); + } +} \ No newline at end of file diff --git a/examples/Flyway/tests/Flyway.Tests/Usings.cs b/examples/Flyway/tests/Flyway.Tests/Usings.cs new file mode 100644 index 000000000..557c05167 --- /dev/null +++ b/examples/Flyway/tests/Flyway.Tests/Usings.cs @@ -0,0 +1,11 @@ +global using System; +global using System.Data.Common; +global using System.Threading.Tasks; +global using DotNet.Testcontainers.Builders; +global using DotNet.Testcontainers.Configurations; +global using DotNet.Testcontainers.Containers; +global using DotNet.Testcontainers.Networks; +global using JetBrains.Annotations; +global using Npgsql; +global using Testcontainers.PostgreSql; +global using Xunit; \ No newline at end of file diff --git a/examples/Flyway/tests/Flyway.Tests/migrate/V1.0__create_users_table.sql b/examples/Flyway/tests/Flyway.Tests/migrate/V1.0__create_users_table.sql new file mode 100644 index 000000000..8cbb71060 --- /dev/null +++ b/examples/Flyway/tests/Flyway.Tests/migrate/V1.0__create_users_table.sql @@ -0,0 +1,7 @@ +CREATE TABLE users +( + id SERIAL PRIMARY KEY, + username VARCHAR(50) NOT NULL, + email VARCHAR(100) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/examples/Flyway/tests/Flyway.Tests/migrate/V1.1__alter_users_table.sql b/examples/Flyway/tests/Flyway.Tests/migrate/V1.1__alter_users_table.sql new file mode 100644 index 000000000..81ee4fe00 --- /dev/null +++ b/examples/Flyway/tests/Flyway.Tests/migrate/V1.1__alter_users_table.sql @@ -0,0 +1 @@ +ALTER TABLE users ADD COLUMN age INT; \ No newline at end of file diff --git a/examples/Flyway/tests/Flyway.Tests/migrate/V2.0__seed_example_users.sql b/examples/Flyway/tests/Flyway.Tests/migrate/V2.0__seed_example_users.sql new file mode 100644 index 000000000..0e4dfa7cc --- /dev/null +++ b/examples/Flyway/tests/Flyway.Tests/migrate/V2.0__seed_example_users.sql @@ -0,0 +1 @@ +INSERT INTO users (username, email, age) VALUES ('john_doe', 'john@example.com', 30); \ No newline at end of file diff --git a/tests/Testcontainers.PostgreSql.Tests/Testcontainers.PostgreSql.Tests.csproj b/tests/Testcontainers.PostgreSql.Tests/Testcontainers.PostgreSql.Tests.csproj index cea7f9ed1..863363361 100644 --- a/tests/Testcontainers.PostgreSql.Tests/Testcontainers.PostgreSql.Tests.csproj +++ b/tests/Testcontainers.PostgreSql.Tests/Testcontainers.PostgreSql.Tests.csproj @@ -9,7 +9,7 @@ - +