CamusDB.Testcontainers.XunitV3 0.17.0

dotnet add package CamusDB.Testcontainers.XunitV3 --version 0.17.0
                    
NuGet\Install-Package CamusDB.Testcontainers.XunitV3 -Version 0.17.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CamusDB.Testcontainers.XunitV3" Version="0.17.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CamusDB.Testcontainers.XunitV3" Version="0.17.0" />
                    
Directory.Packages.props
<PackageReference Include="CamusDB.Testcontainers.XunitV3" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CamusDB.Testcontainers.XunitV3 --version 0.17.0
                    
#r "nuget: CamusDB.Testcontainers.XunitV3, 0.17.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CamusDB.Testcontainers.XunitV3@0.17.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CamusDB.Testcontainers.XunitV3&version=0.17.0
                    
Install as a Cake Addin
#tool nuget:?package=CamusDB.Testcontainers.XunitV3&version=0.17.0
                    
Install as a Cake Tool

CamusDB Testcontainers

Testcontainers modules for CamusDB. A module starts a temporary CamusDB server in Docker for an integration test. When the test ends, the module removes the container.

Package Use
CamusDB.Testcontainers The module: CamusDbBuilder and CamusDbContainer. It works with any test framework.
CamusDB.Testcontainers.Xunit CamusDbFixture and CamusDbAdoNetFixture for xUnit.net v2. They are built on Testcontainers.Xunit.
CamusDB.Testcontainers.XunitV3 CamusDbFixture and CamusDbAdoNetFixture for xUnit.net v3. They are built on Testcontainers.XunitV3.

All packages target .NET 8, .NET 9 and .NET 10. They need a Docker host that Testcontainers can find.

Quick start

dotnet add package CamusDB.Testcontainers
using CamusDB.Testcontainers;

await using CamusDbContainer camusDb = new CamusDbBuilder().Build();
await camusDb.StartAsync();

await camusDb.CreateDatabaseAsync("shop");
await camusDb.ExecuteDdlAsync("shop", "CREATE TABLE products (id int64 PRIMARY KEY NOT NULL, name STRING NOT NULL)");
await camusDb.ExecuteNonQueryAsync("shop", "INSERT INTO products (id, name) VALUES (1, 'kettle')");

string json = await camusDb.ExecuteQueryAsync("shop", "SELECT name FROM products");

StartAsync returns when the server can run a statement. The /health endpoint is not a sufficient check, because the HTTP listener starts before the storage node. Thus the module sends a real request (SHOW DATABASES, or a login when authentication is on) until the request succeeds.

Connection strings

The container returns connection strings for CamusDB.Client, the CamusDB ADO.NET provider. The packages do not reference CamusDB.Client. Add it to your test project yourself:

dotnet add package CamusDB.Client
using CamusDB.Client;

await using CamusConnection connection = new(new CamusConnectionStringBuilder(camusDb.GetConnectionString("shop")));
await connection.OpenAsync();

await using CamusCommand select = connection.CreateCamusCommand("SELECT name FROM products");
await using CamusDataReader reader = await select.ExecuteReaderAsync();
Method Returns
GetConnectionString() A connection string for the JSON/HTTP API, without a database: Endpoint=http://127.0.0.1:32768.
GetConnectionString(database) The same string with the database: Endpoint=http://127.0.0.1:32768;Database=shop.
GetGrpcConnectionString(database) A connection string for the gRPC API: Endpoint=http://127.0.0.1:32769;Protocol=grpc;BackupEndpoint=http://127.0.0.1:32768;Database=shop. The database is optional.
GetHttpAddress() The JSON/HTTP API address only, e.g. http://127.0.0.1:32768.
GetGrpcAddress() The gRPC API address only, e.g. http://127.0.0.1:32769. The port uses plaintext HTTP/2.

The container maps each API port to a random host port. Thus, get the strings after StartAsync.

The gRPC string sets BackupEndpoint to the JSON/HTTP API, because the backup admin API is REST only.

When authentication is on, each connection string also contains these settings:

  • User and Password: the bootstrap superuser credentials.
  • AllowInsecureCredentials=true. The client refuses to send credentials in plaintext to an endpoint that is not loopback. On a remote Docker host, the container endpoint is not loopback.

The tests of this repository use CamusDB.Client 0.16.0 with both strings, with and without authentication. CamusDB.Dapper and CamusDB.EntityFrameworkCore use CamusDB.Client, so they take the same strings. These two packages are not tested here.

Helper methods

The helper methods use the JSON/HTTP API. Each method returns the JSON response body as a string. If the server returns a status code that is not 2xx, the method throws InvalidOperationException.

Method HTTP endpoint
CreateDatabaseAsync(name) /create-db. If the database exists, the method does nothing.
ExecuteDdlAsync(database, sql) /execute-sql-ddl
ExecuteNonQueryAsync(database, sql) /execute-sql-non-query
ExecuteQueryAsync(database, sql) /execute-sql-query
GetAccessTokenAsync() /login. It returns a bearer token. Use it only when authentication is on.

Configuration

Builder method Effect
new CamusDbBuilder(image) Sets the image. The default is camusdb/camusdb:0.15.3.
WithAuthentication(username, password) Turns on authentication and creates a bootstrap superuser.
WithMemoryProfile("dev") Limits the server caches to approximately 96 MiB. Use it when one host runs many containers.
WithPartitions(n) Sets the number of Raft partitions. The image default is 4.
WithSetting(key, value) Writes one setting to the server config.yml.

All the usual Testcontainers builder methods are also available, for example WithEnvironment and WithReuse.

Authentication

CamusDbContainer camusDb = new CamusDbBuilder()
    .WithAuthentication("admin", "s3cret-test-password")
    .Build();

The module sets the CamusDB authentication environment variables. It also generates a random access-token server key. The helper methods log in as the bootstrap superuser and keep the token until 30 seconds before it expires.

The module also sets require_tls_when_auth_enabled: false. The server refuses credentials over plaintext HTTP from a non-loopback address. A request from the test host comes from such an address, because Docker forwards it.

Server settings

WithSetting writes each setting to /etc/camusdb/config.yml in the container. The module then points CAMUS_CONFIG_PATH at that file. A dotted key becomes a nested YAML mapping:

new CamusDbBuilder()
    .WithSetting("kahuna.backup_dir", "/data/backups")
    .WithSetting("query_result_cache_enabled", "false");

The module writes each value as a raw YAML scalar. Thus true stays a boolean and 4 stays a number.

The image command line sets the HTTP port, the data directory and the partition count. The command line overrides config.yml, so WithSetting cannot change these three values. Use WithPartitions for the partition count.

xUnit.net fixture

dotnet add package CamusDB.Testcontainers.XunitV3   # or CamusDB.Testcontainers.Xunit for xUnit.net v2

CamusDbFixture starts one container for all the tests of a class. To prepare the database before the first test, override InitializeDatabaseAsync:

using CamusDB.Testcontainers;
using CamusDB.Testcontainers.Xunit;

public sealed class InventoryFixture(IMessageSink messageSink) : CamusDbFixture(messageSink)
{
    protected override CamusDbBuilder Configure()
    {
        return new CamusDbBuilder().WithMemoryProfile("dev");
    }

    protected override async Task InitializeDatabaseAsync(CamusDbContainer container, CancellationToken ct)
    {
        await container.CreateDatabaseAsync("inventory", ct);
        await container.ExecuteDdlAsync("inventory", "CREATE TABLE items (id int64 PRIMARY KEY NOT NULL, name STRING NOT NULL)", ct);
    }
}

public sealed class InventoryTests(InventoryFixture fixture) : IClassFixture<InventoryFixture>
{
    [Fact]
    public async Task ReadsItems()
    {
        string json = await fixture.Container.ExecuteQueryAsync("inventory", "SELECT * FROM items");
        // ...
    }
}

To share one container across many test classes, use an xUnit collection fixture. xUnit requires the [CollectionDefinition] class to be in the test assembly, so declare it in your test project:

[CollectionDefinition(nameof(CamusDbCollection))]
public sealed class CamusDbCollection : ICollectionFixture<InventoryFixture>;

[Collection(nameof(CamusDbCollection))]
public sealed class OrderTests(InventoryFixture fixture) { /* ... */ }

If the container does not start, each test that reads fixture.Container fails with the start exception.

ADO.NET fixture

CamusDbAdoNetFixture extends the Testcontainers DbContainerFixture. It gives the tests ADO.NET connections and commands: OpenConnectionAsync(), CreateConnection() and CreateCommand(sql). It needs CamusProviderFactory, the provider factory of CamusDB.Client.

CamusProviderFactory is not in CamusDB.Client 0.16.0. It comes in the next release of CamusDB.Client.

The fixture package does not reference CamusDB.Client. Your fixture supplies the factory, so your test project selects the client version:

using System.Data.Common;
using CamusDB.Client;
using CamusDB.Testcontainers;
using CamusDB.Testcontainers.Xunit;

public sealed class RobotsFixture(IMessageSink messageSink) : CamusDbAdoNetFixture(messageSink)
{
    public override DbProviderFactory DbProviderFactory => CamusProviderFactory.Instance;

    protected override async Task InitializeDatabaseAsync(CamusDbContainer container, CancellationToken ct)
    {
        await using DbCommand create = CreateCommand("CREATE TABLE robots (id int64 PRIMARY KEY NOT NULL, name STRING NOT NULL)");
        await create.ExecuteNonQueryAsync(ct);
    }
}

public sealed class RobotTests(RobotsFixture fixture) : IClassFixture<RobotsFixture>
{
    [Fact]
    public async Task InsertsARobot()
    {
        await using DbCommand insert = fixture.CreateCommand("INSERT INTO robots (id, name) VALUES (1, 'R2-D2')");
        Assert.Equal(1, await insert.ExecuteNonQueryAsync());
    }
}

When the container starts, the fixture creates the database DatabaseName. The default name is test. Then the fixture calls InitializeDatabaseAsync. The fixture ConnectionString names that database, because a CamusDB connection needs a database.

The fixture uses the JSON/HTTP API. To use the gRPC API, override ConnectionString:

public override string ConnectionString => Container.GetGrpcConnectionString(DatabaseName);

Build and test

dotnet build
dotnet test      # needs a running Docker host
dotnet pack -c Release   # writes the packages to artifacts/packages

scripts/pack-nuget.sh runs the unit tests and packs all the packages to artifacts/packages. It does not push them. Set VERSION=0.2.0 to pack a different version. Set TESTS=0 to skip the tests.

Release

The release workflow publishes the packages to nuget.org when you push a tag such as v0.1.0. The tag sets the package version. The workflow needs a NUGET_API_KEY repository secret.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.17.0 50 11/5/2026