Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,16 @@
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="$(DependencyInjectionPackageVersion)" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="$(LoggingPackageVersion)" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="$(LoggingPackageVersion)" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="NUnit3TestAdapter" Version="6.1.0" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="GitHubActionsTestLogger" Version="3.0.1" />
<PackageVersion Include="AdoNet.Specification.Tests" Version="2.0.0-beta.2" />
<PackageVersion Include="OpenTelemetry" Version="1.15.0" />
<PackageVersion Include="OpenTelemetry.Exporter.InMemory" Version="1.15.0" />
<PackageVersion Include="Testcontainers" Version="4.10.0" />
<PackageVersion Include="Testcontainers.PostgreSql" Version="4.10.0" />

<!-- Benchmarks -->
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
Expand Down
2 changes: 2 additions & 0 deletions test/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@

<ItemGroup>
<PackageReference Include="GitHubActionsTestLogger" />
<PackageReference Include="Testcontainers" />
<PackageReference Include="Testcontainers.PostgreSql" />
</ItemGroup>
</Project>
112 changes: 112 additions & 0 deletions test/Npgsql.DependencyInjection.Tests/AssemblySetUp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using DotNet.Testcontainers.Configurations;
using Npgsql;
using Npgsql.Tests;
using NUnit.Framework;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Testcontainers.PostgreSql;

[SetUpFixture]
public class AssemblySetUp
{
static PostgreSqlContainer? _container;

[OneTimeSetUp]
public async Task Setup()
{
var connString = TestUtil.ConnectionString;
using var conn = new NpgsqlConnection(connString);
try
{
conn.Open();
}
catch (NpgsqlException e) when (e.IsTransient)
{
_container ??= SetupContainerAsync();
await _container.StartAsync();

var containerConnString = _container.GetConnectionString();
Environment.SetEnvironmentVariable("NPGSQL_TEST_DB", containerConnString);
await using var containerConn = new NpgsqlConnection(containerConnString);
await containerConn.OpenAsync();
return;
}
catch (PostgresException e)
{
if (e.SqlState == PostgresErrorCodes.InvalidPassword && connString == TestUtil.DefaultConnectionString)
throw new Exception("Please create a user npgsql_tests as follows: CREATE USER npgsql_tests PASSWORD 'npgsql_tests' SUPERUSER");

if (e.SqlState == PostgresErrorCodes.InvalidCatalogName)
{
var builder = new NpgsqlConnectionStringBuilder(connString)
{
Pooling = false,
Multiplexing = false,
Database = "postgres"
};

using var adminConn = new NpgsqlConnection(builder.ConnectionString);
adminConn.Open();
adminConn.ExecuteNonQuery("CREATE DATABASE " + conn.Database);
adminConn.Close();
Thread.Sleep(1000);

conn.Open();
return;
}

throw;
}
}

[OneTimeTearDown]
public async Task Teardown()
{
if (_container != null)
{
await _container.DisposeAsync();
}
}

static PostgreSqlContainer SetupContainerAsync()
{
var repoRoot = GetRepoRoot();
var initScriptPath = Path.Combine(repoRoot, "test", "containers", "postgres", "init-db.sh");
var certsPath = Path.Combine(repoRoot, ".build");

if (!File.Exists(initScriptPath))
throw new InvalidOperationException($"Init script not found: {initScriptPath}");
if (!Directory.Exists(certsPath))
throw new InvalidOperationException($"Certs directory not found: {certsPath}");

var image = Environment.GetEnvironmentVariable("NPGSQL_TEST_IMAGE") ?? "postgres:18";

var builder = new PostgreSqlBuilder(image)
.WithDatabase("npgsql_tests")
.WithUsername("npgsql_tests")
.WithPassword("npgsql_tests")
.WithPortBinding(5432, false)
.WithBindMount(certsPath, "/certs", AccessMode.ReadOnly)
.WithBindMount(initScriptPath, "/docker-entrypoint-initdb.d/01-init-db.sh", AccessMode.ReadOnly);

if (!OperatingSystem.IsWindows())
builder = builder.WithBindMount("/tmp", "/tmp");

return builder.Build();
}

static string GetRepoRoot()
{
var dir = new DirectoryInfo(AppContext.BaseDirectory);
while (dir != null)
{
if (File.Exists(Path.Combine(dir.FullName, "Npgsql.slnx")) || Directory.Exists(Path.Combine(dir.FullName, ".git")))
return dir.FullName;
dir = dir.Parent;
}

throw new InvalidOperationException("Could not locate repo root for testcontainers assets.");
}
}
112 changes: 112 additions & 0 deletions test/Npgsql.PluginTests/AssemblySetUp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using DotNet.Testcontainers.Configurations;
using Npgsql;
using Npgsql.Tests;
using NUnit.Framework;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Testcontainers.PostgreSql;

[SetUpFixture]
public class AssemblySetUp
{
static PostgreSqlContainer? _container;

[OneTimeSetUp]
public async Task Setup()
{
var connString = TestUtil.ConnectionString;
using var conn = new NpgsqlConnection(connString);
try
{
conn.Open();
}
catch (NpgsqlException e) when (e.IsTransient)
{
_container ??= SetupContainerAsync();
await _container.StartAsync();

var containerConnString = _container.GetConnectionString();
Environment.SetEnvironmentVariable("NPGSQL_TEST_DB", containerConnString);
await using var containerConn = new NpgsqlConnection(containerConnString);
await containerConn.OpenAsync();
return;
}
catch (PostgresException e)
{
if (e.SqlState == PostgresErrorCodes.InvalidPassword && connString == TestUtil.DefaultConnectionString)
throw new Exception("Please create a user npgsql_tests as follows: CREATE USER npgsql_tests PASSWORD 'npgsql_tests' SUPERUSER");

if (e.SqlState == PostgresErrorCodes.InvalidCatalogName)
{
var builder = new NpgsqlConnectionStringBuilder(connString)
{
Pooling = false,
Multiplexing = false,
Database = "postgres"
};

using var adminConn = new NpgsqlConnection(builder.ConnectionString);
adminConn.Open();
adminConn.ExecuteNonQuery("CREATE DATABASE " + conn.Database);
adminConn.Close();
Thread.Sleep(1000);

conn.Open();
return;
}

throw;
}
}

[OneTimeTearDown]
public async Task Teardown()
{
if (_container != null)
{
await _container.DisposeAsync();
}
}

static PostgreSqlContainer SetupContainerAsync()
{
var repoRoot = GetRepoRoot();
var initScriptPath = Path.Combine(repoRoot, "test", "containers", "postgres", "init-db.sh");
var certsPath = Path.Combine(repoRoot, ".build");

if (!File.Exists(initScriptPath))
throw new InvalidOperationException($"Init script not found: {initScriptPath}");
if (!Directory.Exists(certsPath))
throw new InvalidOperationException($"Certs directory not found: {certsPath}");

var image = Environment.GetEnvironmentVariable("NPGSQL_TEST_IMAGE") ?? "postgres:18";

var builder = new PostgreSqlBuilder(image)
.WithDatabase("npgsql_tests")
.WithUsername("npgsql_tests")
.WithPassword("npgsql_tests")
.WithPortBinding(5432, false)
.WithBindMount(certsPath, "/certs", AccessMode.ReadOnly)
.WithBindMount(initScriptPath, "/docker-entrypoint-initdb.d/01-init-db.sh", AccessMode.ReadOnly);

if (!OperatingSystem.IsWindows())
builder = builder.WithBindMount("/tmp", "/tmp");

return builder.Build();
}

static string GetRepoRoot()
{
var dir = new DirectoryInfo(AppContext.BaseDirectory);
while (dir != null)
{
if (File.Exists(Path.Combine(dir.FullName, "Npgsql.slnx")) || Directory.Exists(Path.Combine(dir.FullName, ".git")))
return dir.FullName;
dir = dir.Parent;
}

throw new InvalidOperationException("Could not locate repo root for testcontainers assets.");
}
}
86 changes: 84 additions & 2 deletions test/Npgsql.Specification.Tests/NpgsqlDbFactoryFixture.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,98 @@
using System;
using System.Data.Common;
using System.IO;
using System.Net.Sockets;
using System.Threading.Tasks;
using AdoNet.Specification.Tests;
using DotNet.Testcontainers.Configurations;
using Testcontainers.PostgreSql;
using Xunit;

namespace Npgsql.Specification.Tests;

public class NpgsqlDbFactoryFixture : IDbFactoryFixture
public class NpgsqlDbFactoryFixture : IDbFactoryFixture, IAsyncLifetime
{
public DbProviderFactory Factory => NpgsqlFactory.Instance;

const string DefaultConnectionString =
"Server=localhost;Username=npgsql_tests;Password=npgsql_tests;Database=npgsql_tests;Timeout=0;Command Timeout=0";

static readonly Lazy<Task> _initializeTask = new(() => InitializeCoreAsync());
public string ConnectionString =>
Environment.GetEnvironmentVariable("NPGSQL_TEST_DB") ?? DefaultConnectionString;
}
static PostgreSqlContainer? _container;

public NpgsqlDbFactoryFixture() => EnsureInitialized();

public Task InitializeAsync() => _initializeTask.Value;

public Task DisposeAsync() => Task.CompletedTask;

static void EnsureInitialized() => _initializeTask.Value.GetAwaiter().GetResult();

static async Task InitializeCoreAsync()
{
var connString = Environment.GetEnvironmentVariable("NPGSQL_TEST_DB") ?? DefaultConnectionString;
await using var conn = new NpgsqlConnection(connString);
try
{
await conn.OpenAsync();
}
catch (NpgsqlException e) when (e.InnerException is SocketException)
{
_container ??= SetupContainer();
await _container.StartAsync();

var containerConnString = _container.GetConnectionString();
Environment.SetEnvironmentVariable("NPGSQL_TEST_DB", containerConnString);

await using var containerConn = new NpgsqlConnection(containerConnString);
await containerConn.OpenAsync();
await containerConn.CloseAsync();
}
finally
{
await conn.CloseAsync();
}
}

static PostgreSqlContainer SetupContainer()
{
var repoRoot = GetRepoRoot();
var initScriptPath = Path.Combine(repoRoot, "test", "containers", "postgres", "init-db.sh");
var certsPath = Path.Combine(repoRoot, ".build");

if (!File.Exists(initScriptPath))
throw new InvalidOperationException($"Init script not found: {initScriptPath}");
if (!Directory.Exists(certsPath))
throw new InvalidOperationException($"Certs directory not found: {certsPath}");

var image = Environment.GetEnvironmentVariable("NPGSQL_TEST_IMAGE") ?? "postgres:18";

var builder = new PostgreSqlBuilder(image)
.WithDatabase("npgsql_tests")
.WithUsername("npgsql_tests")
.WithPassword("npgsql_tests")
.WithPortBinding(5432, false)
.WithBindMount(certsPath, "/certs", AccessMode.ReadOnly)
.WithBindMount(initScriptPath, "/docker-entrypoint-initdb.d/01-init-db.sh", AccessMode.ReadOnly);

if (!OperatingSystem.IsWindows())
builder = builder.WithBindMount("/tmp", "/tmp");

return builder.Build();
}

static string GetRepoRoot()
{
var dir = new DirectoryInfo(AppContext.BaseDirectory);
while (dir != null)
{
if (File.Exists(Path.Combine(dir.FullName, "Npgsql.slnx")) || Directory.Exists(Path.Combine(dir.FullName, ".git")))
return dir.FullName;
dir = dir.Parent;
}

throw new InvalidOperationException("Could not locate repo root for testcontainers assets.");
}
}
Loading