Skip to content

Commit

Permalink
feat: Replace PostgreSQL module (#772)
Browse files Browse the repository at this point in the history
  • Loading branch information
HofmeisterAn authored Feb 10, 2023
1 parent c9bc198 commit 07b5c8c
Show file tree
Hide file tree
Showing 17 changed files with 354 additions and 200 deletions.
1 change: 0 additions & 1 deletion Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
<PackageReference Update="MongoDB.Driver" Version="2.17.1" />
<PackageReference Update="MyCouch" Version="7.6.0" />
<PackageReference Update="Neo4j.Driver" Version="4.4.0" />
<PackageReference Update="Npgsql" Version="6.0.6" />
<PackageReference Update="RabbitMQ.Client" Version="6.2.4" />
</ItemGroup>
</Project>
14 changes: 14 additions & 0 deletions Testcontainers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.MySql", "src
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Oracle", "src\Testcontainers.Oracle\Testcontainers.Oracle.csproj", "{596EAFC1-0496-495C-B382-D57415FA456A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.PostgreSql", "src\Testcontainers.PostgreSql\Testcontainers.PostgreSql.csproj", "{8AB91636-9055-4900-A72A-7CFFACDFDBF0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.RavenDb", "src\Testcontainers.RavenDb\Testcontainers.RavenDb.csproj", "{F6394475-D6F1-46E2-81BF-4BA78A40B878}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Redis", "src\Testcontainers.Redis\Testcontainers.Redis.csproj", "{BFDA179A-40EB-4CEB-B8E9-0DF32C65E2C5}"
Expand All @@ -35,6 +37,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.MySql.Tests"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Oracle.Tests", "tests\Testcontainers.Oracle.Tests\Testcontainers.Oracle.Tests.csproj", "{4AC1088B-9965-4497-AC8E-570F1AD5631F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.PostgreSql.Tests", "tests\Testcontainers.PostgreSql.Tests\Testcontainers.PostgreSql.Tests.csproj", "{56D0DCA5-567F-4B3B-8B79-CB108F8EB8A6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.RavenDb.Tests", "tests\Testcontainers.RavenDb.Tests\Testcontainers.RavenDb.Tests.csproj", "{D53726B6-5447-47E6-B881-A44EFF6E5534}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Redis.Tests", "tests\Testcontainers.Redis.Tests\Testcontainers.Redis.Tests.csproj", "{31EE94A0-E721-4073-B6F1-DD912D004DEF}"
Expand Down Expand Up @@ -77,6 +81,10 @@ Global
{596EAFC1-0496-495C-B382-D57415FA456A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{596EAFC1-0496-495C-B382-D57415FA456A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{596EAFC1-0496-495C-B382-D57415FA456A}.Release|Any CPU.Build.0 = Release|Any CPU
{8AB91636-9055-4900-A72A-7CFFACDFDBF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8AB91636-9055-4900-A72A-7CFFACDFDBF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8AB91636-9055-4900-A72A-7CFFACDFDBF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8AB91636-9055-4900-A72A-7CFFACDFDBF0}.Release|Any CPU.Build.0 = Release|Any CPU
{F6394475-D6F1-46E2-81BF-4BA78A40B878}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F6394475-D6F1-46E2-81BF-4BA78A40B878}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F6394475-D6F1-46E2-81BF-4BA78A40B878}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -105,6 +113,10 @@ Global
{4AC1088B-9965-4497-AC8E-570F1AD5631F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4AC1088B-9965-4497-AC8E-570F1AD5631F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4AC1088B-9965-4497-AC8E-570F1AD5631F}.Release|Any CPU.Build.0 = Release|Any CPU
{56D0DCA5-567F-4B3B-8B79-CB108F8EB8A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{56D0DCA5-567F-4B3B-8B79-CB108F8EB8A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{56D0DCA5-567F-4B3B-8B79-CB108F8EB8A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{56D0DCA5-567F-4B3B-8B79-CB108F8EB8A6}.Release|Any CPU.Build.0 = Release|Any CPU
{D53726B6-5447-47E6-B881-A44EFF6E5534}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D53726B6-5447-47E6-B881-A44EFF6E5534}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D53726B6-5447-47E6-B881-A44EFF6E5534}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -144,13 +156,15 @@ Global
{121FB123-40D9-44D4-9AB7-AD57ED34F466} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{9FDCFAEA-AE42-4C69-89EF-F1FF75E88CCC} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{596EAFC1-0496-495C-B382-D57415FA456A} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{8AB91636-9055-4900-A72A-7CFFACDFDBF0} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{F6394475-D6F1-46E2-81BF-4BA78A40B878} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{BFDA179A-40EB-4CEB-B8E9-0DF32C65E2C5} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{2478673C-B063-469D-ABD1-0C3E0A25541B} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{7F0AE083-9DB8-4BD4-91F7-C199DCC7301D} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{25DBED78-99F4-433F-BBF5-1B4E9DEAE437} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{E42DA1CE-698F-4E45-8D1F-5D5895893840} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{4AC1088B-9965-4497-AC8E-570F1AD5631F} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{56D0DCA5-567F-4B3B-8B79-CB108F8EB8A6} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{D53726B6-5447-47E6-B881-A44EFF6E5534} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{31EE94A0-E721-4073-B6F1-DD912D004DEF} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{DA1D7ADE-452C-4369-83CC-56289176EACD} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
Expand Down
1 change: 1 addition & 0 deletions src/Testcontainers.PostgreSql/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
122 changes: 122 additions & 0 deletions src/Testcontainers.PostgreSql/PostgreSqlBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
namespace Testcontainers.PostgreSql;

/// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" />
[PublicAPI]
public sealed class PostgreSqlBuilder : ContainerBuilder<PostgreSqlBuilder, PostgreSqlContainer, PostgreSqlConfiguration>
{
public const string PostgreSqlImage = "postgres:15.1";

public const ushort PostgreSqlPort = 5432;

/// <summary>
/// Initializes a new instance of the <see cref="PostgreSqlBuilder" /> class.
/// </summary>
public PostgreSqlBuilder()
: this(new PostgreSqlConfiguration())
{
DockerResourceConfiguration = Init().DockerResourceConfiguration;
}

/// <summary>
/// Initializes a new instance of the <see cref="PostgreSqlBuilder" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
private PostgreSqlBuilder(PostgreSqlConfiguration resourceConfiguration)
: base(resourceConfiguration)
{
DockerResourceConfiguration = resourceConfiguration;
}

/// <inheritdoc />
protected override PostgreSqlConfiguration DockerResourceConfiguration { get; }

/// <summary>
/// Sets the PostgreSql database.
/// </summary>
/// <remarks>
/// The Docker image does not allow to configure the database.
/// </remarks>
/// <param name="database">The PostgreSql database.</param>
/// <returns>A configured instance of <see cref="PostgreSqlBuilder" />.</returns>
public PostgreSqlBuilder WithDatabase(string database)
{
return Merge(DockerResourceConfiguration, new PostgreSqlConfiguration(database: database))
.WithEnvironment("POSTGRES_DB", database);
}

/// <summary>
/// Sets the PostgreSql username.
/// </summary>
/// <remarks>
/// The Docker image does not allow to configure the username.
/// </remarks>
/// <param name="username">The PostgreSql username.</param>
/// <returns>A configured instance of <see cref="PostgreSqlBuilder" />.</returns>
public PostgreSqlBuilder WithUsername(string username)
{
return Merge(DockerResourceConfiguration, new PostgreSqlConfiguration(username: username))
.WithEnvironment("POSTGRES_USER", username);
}

/// <summary>
/// Sets the PostgreSql password.
/// </summary>
/// <param name="password">The PostgreSql password.</param>
/// <returns>A configured instance of <see cref="PostgreSqlBuilder" />.</returns>
public PostgreSqlBuilder WithPassword(string password)
{
return Merge(DockerResourceConfiguration, new PostgreSqlConfiguration(password: password))
.WithEnvironment("POSTGRES_PASSWORD", password);
}

/// <inheritdoc />
public override PostgreSqlContainer Build()
{
Validate();
return new PostgreSqlContainer(DockerResourceConfiguration, TestcontainersSettings.Logger);
}

/// <inheritdoc />
protected override PostgreSqlBuilder Init()
{
return base.Init()
.WithImage(PostgreSqlImage)
.WithPortBinding(PostgreSqlPort, true)
.WithDatabase("postgres")
.WithUsername("postgres")
.WithPassword(Guid.NewGuid().ToString("D"))
// Disable durability: https://www.postgresql.org/docs/current/non-durability.html.
.WithCommand("-c", "fsync=off")
.WithCommand("-c", "full_page_writes=off")
.WithCommand("-c", "synchronous_commit=off")
.WithWaitStrategy(Wait.ForUnixContainer().UntilCommandIsCompleted("pg_isready"));
}

/// <inheritdoc />
protected override void Validate()
{
base.Validate();

_ = Guard.Argument(DockerResourceConfiguration.Password, nameof(DockerResourceConfiguration.Password))
.NotNull()
.NotEmpty();
}

/// <inheritdoc />
protected override PostgreSqlBuilder Clone(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
{
return Merge(DockerResourceConfiguration, new PostgreSqlConfiguration(resourceConfiguration));
}

/// <inheritdoc />
protected override PostgreSqlBuilder Clone(IContainerConfiguration resourceConfiguration)
{
return Merge(DockerResourceConfiguration, new PostgreSqlConfiguration(resourceConfiguration));
}

/// <inheritdoc />
protected override PostgreSqlBuilder Merge(PostgreSqlConfiguration oldValue, PostgreSqlConfiguration newValue)
{
return new PostgreSqlBuilder(new PostgreSqlConfiguration(oldValue, newValue));
}
}
80 changes: 80 additions & 0 deletions src/Testcontainers.PostgreSql/PostgreSqlConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
namespace Testcontainers.PostgreSql;

/// <inheritdoc cref="ContainerConfiguration" />
[PublicAPI]
public sealed class PostgreSqlConfiguration : ContainerConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="PostgreSqlConfiguration" /> class.
/// </summary>
/// <param name="database">The PostgreSql database.</param>
/// <param name="username">The PostgreSql username.</param>
/// <param name="password">The PostgreSql password.</param>
public PostgreSqlConfiguration(
string database = null,
string username = null,
string password = null)
{
Database = database;
Username = username;
Password = password;
}

/// <summary>
/// Initializes a new instance of the <see cref="PostgreSqlConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public PostgreSqlConfiguration(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
: base(resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}

/// <summary>
/// Initializes a new instance of the <see cref="PostgreSqlConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public PostgreSqlConfiguration(IContainerConfiguration resourceConfiguration)
: base(resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}

/// <summary>
/// Initializes a new instance of the <see cref="PostgreSqlConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public PostgreSqlConfiguration(PostgreSqlConfiguration resourceConfiguration)
: this(new PostgreSqlConfiguration(), resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}

/// <summary>
/// Initializes a new instance of the <see cref="PostgreSqlConfiguration" /> class.
/// </summary>
/// <param name="oldValue">The old Docker resource configuration.</param>
/// <param name="newValue">The new Docker resource configuration.</param>
public PostgreSqlConfiguration(PostgreSqlConfiguration oldValue, PostgreSqlConfiguration newValue)
: base(oldValue, newValue)
{
Database = BuildConfiguration.Combine(oldValue.Database, newValue.Database);
Username = BuildConfiguration.Combine(oldValue.Username, newValue.Username);
Password = BuildConfiguration.Combine(oldValue.Password, newValue.Password);
}

/// <summary>
/// Gets the PostgreSql database.
/// </summary>
public string Database { get; }

/// <summary>
/// Gets the PostgreSql username.
/// </summary>
public string Username { get; }

/// <summary>
/// Gets the PostgreSql password.
/// </summary>
public string Password { get; }
}
51 changes: 51 additions & 0 deletions src/Testcontainers.PostgreSql/PostgreSqlContainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
namespace Testcontainers.PostgreSql;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class PostgreSqlContainer : DockerContainer
{
private readonly PostgreSqlConfiguration _configuration;

/// <summary>
/// Initializes a new instance of the <see cref="PostgreSqlContainer" /> class.
/// </summary>
/// <param name="configuration">The container configuration.</param>
/// <param name="logger">The logger.</param>
public PostgreSqlContainer(PostgreSqlConfiguration configuration, ILogger logger)
: base(configuration, logger)
{
_configuration = configuration;
}

/// <summary>
/// Gets the PostgreSql connection string.
/// </summary>
/// <returns>The PostgreSql connection string.</returns>
public string GetConnectionString()
{
var properties = new Dictionary<string, string>();
properties.Add("Host", Hostname);
properties.Add("Port", GetMappedPublicPort(PostgreSqlBuilder.PostgreSqlPort).ToString());
properties.Add("Database", _configuration.Database);
properties.Add("Username", _configuration.Username);
properties.Add("Password", _configuration.Password);
return string.Join(";", properties.Select(property => string.Join("=", property.Key, property.Value)));
}

/// <summary>
/// Executes the SQL script in the PostgreSql container.
/// </summary>
/// <param name="scriptContent">The content of the SQL script to execute.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>Task that completes when the SQL script has been executed.</returns>
public async Task<ExecResult> ExecScriptAsync(string scriptContent, CancellationToken ct = default)
{
var scriptFilePath = string.Join("/", string.Empty, "tmp", Guid.NewGuid().ToString("D"), Path.GetRandomFileName());

await CopyFileAsync(scriptFilePath, Encoding.Default.GetBytes(scriptContent), 493, 0, 0, ct)
.ConfigureAwait(false);

return await ExecAsync(new[] { "psql", "--username", _configuration.Username, "--dbname", _configuration.Database, "--file", scriptFilePath }, ct)
.ConfigureAwait(false);
}
}
12 changes: 12 additions & 0 deletions src/Testcontainers.PostgreSql/Testcontainers.PostgreSql.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2022.3.1"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)src/Testcontainers/Testcontainers.csproj"/>
</ItemGroup>
</Project>
4 changes: 4 additions & 0 deletions src/Testcontainers.PostgreSql/Usings.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
global using System;
global using System.Collections.Generic;
global using System.IO;
global using System.Linq;
global using System.Text;
global using System.Threading;
global using System.Threading.Tasks;
global using Docker.DotNet.Models;
global using DotNet.Testcontainers;
global using DotNet.Testcontainers.Builders;
Expand Down

This file was deleted.

Loading

0 comments on commit 07b5c8c

Please sign in to comment.