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
1 change: 1 addition & 0 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ jobs:
{ name: "Testcontainers.Couchbase", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.CouchDb", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.Db2", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.DragonflyDb", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.DynamoDb", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.Elasticsearch", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.EventHubs", runs-on: "ubuntu-22.04" },
Expand Down
17 changes: 17 additions & 0 deletions Testcontainers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Xunit.Tests"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.XunitV3.Tests", "tests\Testcontainers.XunitV3.Tests\Testcontainers.XunitV3.Tests.csproj", "{B2E8B7FB-7D1E-4DD3-A25E-34DE4386B1EB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.DragonflyDb", "src\Testcontainers.DragonflyDb\Testcontainers.DragonflyDb.csproj", "{0686A718-3933-D826-BDC5-2F683AB593CD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.DragonflyDb.Tests", "tests\Testcontainers.DragonflyDb.Tests\Testcontainers.DragonflyDb.Tests.csproj", "{546824C2-F360-5F78-1FC6-3D1E191C3FAF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -751,6 +755,14 @@ Global
{B2E8B7FB-7D1E-4DD3-A25E-34DE4386B1EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2E8B7FB-7D1E-4DD3-A25E-34DE4386B1EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2E8B7FB-7D1E-4DD3-A25E-34DE4386B1EB}.Release|Any CPU.Build.0 = Release|Any CPU
{0686A718-3933-D826-BDC5-2F683AB593CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0686A718-3933-D826-BDC5-2F683AB593CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0686A718-3933-D826-BDC5-2F683AB593CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0686A718-3933-D826-BDC5-2F683AB593CD}.Release|Any CPU.Build.0 = Release|Any CPU
{546824C2-F360-5F78-1FC6-3D1E191C3FAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{546824C2-F360-5F78-1FC6-3D1E191C3FAF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{546824C2-F360-5F78-1FC6-3D1E191C3FAF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{546824C2-F360-5F78-1FC6-3D1E191C3FAF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -878,5 +890,10 @@ Global
{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{E901DF14-6F05-4FC2-825A-3055FAD33561} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{B2E8B7FB-7D1E-4DD3-A25E-34DE4386B1EB} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{0686A718-3933-D826-BDC5-2F683AB593CD} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{546824C2-F360-5F78-1FC6-3D1E191C3FAF} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3BA67751-0C2A-4D17-A84E-C0AC59B94254}
EndGlobalSection
EndGlobal
1 change: 1 addition & 0 deletions src/Testcontainers.DragonflyDb/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
66 changes: 66 additions & 0 deletions src/Testcontainers.DragonflyDb/DragonflyDbBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
namespace Testcontainers.DragonflyDb;

/// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" />
[PublicAPI]
public sealed class DragonflyDbBuilder : ContainerBuilder<DragonflyDbBuilder, DragonflyDbContainer, DragonflyDbConfiguration>
{
public const string DragonflyDbImage = "docker.dragonflydb.io/dragonflydb/dragonfly";

public const ushort DragonflyDbPort = 6379;

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

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

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

/// <inheritdoc />
public override DragonflyDbContainer Build()
{
Validate();
return new DragonflyDbContainer(DockerResourceConfiguration);
}

/// <inheritdoc />
protected override DragonflyDbBuilder Init()
{
return base.Init()
.WithImage(DragonflyDbImage)
.WithPortBinding(DragonflyDbPort, true)
.WithWaitStrategy(Wait.ForUnixContainer().UntilCommandIsCompleted("/usr/local/bin/healthcheck.sh"));
}

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

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

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

/// <inheritdoc cref="ContainerConfiguration" />
[PublicAPI]
public sealed class DragonflyDbConfiguration : ContainerConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="DragonflyDbConfiguration" /> class.
/// </summary>
public DragonflyDbConfiguration()
{
}

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

/// <summary>
/// Initializes a new instance of the <see cref="DragonflyDbConfiguration" /> class.
/// </summary>
/// <param name="oldValue">The old Docker resource configuration.</param>
/// <param name="newValue">The new Docker resource configuration.</param>
public DragonflyDbConfiguration(DragonflyDbConfiguration oldValue, DragonflyDbConfiguration newValue)
: base(oldValue, newValue)
{
}
}
42 changes: 42 additions & 0 deletions src/Testcontainers.DragonflyDb/DragonflyDbContainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
namespace Testcontainers.DragonflyDb;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class DragonflyDbContainer : DockerContainer
{
/// <summary>
/// Initializes a new instance of the <see cref="DragonflyDbContainer" /> class.
/// </summary>
/// <param name="configuration">The container configuration.</param>
public DragonflyDbContainer(DragonflyDbConfiguration configuration)
: base(configuration)
{
}

/// <summary>
/// Gets the DragonflyDb connection string.
/// </summary>
/// <returns>The DragonflyDb connection string.</returns>
public string GetConnectionString()
{
return new UriBuilder("DragonflyDb", Hostname, GetMappedPublicPort(DragonflyDbBuilder.DragonflyDbPort)).Uri.Authority;
}

/// <summary>
/// Executes the Lua script in the DragonflyDb container.
/// </summary>
/// <param name="scriptContent">The content of the Lua script to execute.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>Task that completes when the Lua 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 CopyAsync(Encoding.Default.GetBytes(scriptContent), scriptFilePath, fileMode: Unix.FileMode644, ct: ct)
.ConfigureAwait(false);

//DragonflyDb uses the same cli as redis
return await ExecAsync(new[] { "redis-cli", "--eval", scriptFilePath, "0" }, ct)
.ConfigureAwait(false);
}
}
12 changes: 12 additions & 0 deletions src/Testcontainers.DragonflyDb/Testcontainers.DragonflyDb.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" VersionOverride="2023.3.0" PrivateAssets="All"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../Testcontainers/Testcontainers.csproj"/>
</ItemGroup>
</Project>
10 changes: 10 additions & 0 deletions src/Testcontainers.DragonflyDb/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
global using System;
global using System.IO;
global using System.Text;
global using System.Threading;
global using System.Threading.Tasks;
global using Docker.DotNet.Models;
global using DotNet.Testcontainers.Builders;
global using DotNet.Testcontainers.Configurations;
global using DotNet.Testcontainers.Containers;
global using JetBrains.Annotations;
1 change: 1 addition & 0 deletions tests/Testcontainers.DragonflyDb.Tests/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
60 changes: 60 additions & 0 deletions tests/Testcontainers.DragonflyDb.Tests/DragonflyDbContainerTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
namespace Testcontainers.DragonflyDb;

public sealed class DragonflyDbContainerTest : IAsyncLifetime
{
private readonly DragonflyDbContainer _dragonflyDbContainer = new DragonflyDbBuilder().Build();

public async ValueTask InitializeAsync()
{
await _dragonflyDbContainer.StartAsync()
.ConfigureAwait(false);
}

public ValueTask DisposeAsync()
{
return _dragonflyDbContainer.DisposeAsync();
}

[Fact]
[Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
public void ConnectionStateReturnsOpen()
{
using var connection = ConnectionMultiplexer.Connect(_dragonflyDbContainer.GetConnectionString());
Assert.True(connection.IsConnected);
}

[Fact]
[Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
public async Task ExecScriptReturnsSuccessful()
{
// Given
const string scriptContent = "return 'Hello, scripting!'";

// When
var execResult = await _dragonflyDbContainer.ExecScriptAsync(scriptContent, TestContext.Current.CancellationToken)
.ConfigureAwait(true);

// Then
Assert.Equal(0L, execResult.ExitCode);
Assert.Equal("Hello, scripting!\n", execResult.Stdout);
Assert.Empty(execResult.Stderr);
}

[Fact]
[Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
public async Task SetAndGetValueReturnsExpected()
{
// Given
using var connection = ConnectionMultiplexer.Connect(_dragonflyDbContainer.GetConnectionString());
var database = connection.GetDatabase();
const string key = "test-key";
const string value = "test-value";

// When
await database.StringSetAsync(key, value);
var result = await database.StringGetAsync(key);

// Then
Assert.Equal(value, result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net9.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<IsPublishable>false</IsPublishable>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk"/>
<PackageReference Include="coverlet.collector"/>
<PackageReference Include="xunit.runner.visualstudio"/>
<PackageReference Include="xunit.v3"/>
<PackageReference Include="StackExchange.Redis"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../src/Testcontainers.DragonflyDb/Testcontainers.DragonflyDb.csproj"/>
<ProjectReference Include="../Testcontainers.Commons/Testcontainers.Commons.csproj"/>
</ItemGroup>
</Project>
4 changes: 4 additions & 0 deletions tests/Testcontainers.DragonflyDb.Tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
global using System.Threading.Tasks;
global using DotNet.Testcontainers.Commons;
global using StackExchange.Redis;
global using Xunit;