diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml
index e1f122658..a418cf98b 100644
--- a/.github/workflows/cicd.yml
+++ b/.github/workflows/cicd.yml
@@ -42,6 +42,7 @@ jobs:
{ name: "Testcontainers.Azurite", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.BigQuery", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.Bigtable", runs-on: "ubuntu-22.04" },
+ { name: "Testcontainers.Cassandra", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.ClickHouse", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.CockroachDb", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.Consul", runs-on: "ubuntu-22.04" },
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 0a4db1cff..4c8f9839c 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -36,6 +36,7 @@
+
diff --git a/Testcontainers.sln b/Testcontainers.sln
index 96eb3545f..1bac70646 100644
--- a/Testcontainers.sln
+++ b/Testcontainers.sln
@@ -23,6 +23,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.BigQuery", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Bigtable", "src\Testcontainers.Bigtable\Testcontainers.Bigtable.csproj", "{302EC1E0-AE75-4E99-A6BF-524F35338BC8}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Cassandra", "src\Testcontainers.Cassandra\Testcontainers.Cassandra.csproj", "{8495D757-5FD7-491C-B941-9D43B3DCF3C0}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ClickHouse", "src\Testcontainers.ClickHouse\Testcontainers.ClickHouse.csproj", "{B061A78E-536E-4CA1-8401-234D5FBFBAB7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.CockroachDb", "src\Testcontainers.CockroachDb\Testcontainers.CockroachDb.csproj", "{8D9871C6-5A39-4F0B-A15A-E87D34F3EA73}"
@@ -123,6 +125,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.BigQuery.Tes
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Bigtable.Tests", "tests\Testcontainers.Bigtable.Tests\Testcontainers.Bigtable.Tests.csproj", "{2E7B92E3-8526-4706-90F3-00F0F5C47C37}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Cassandra.Tests", "tests\Testcontainers.Cassandra.Tests\Testcontainers.Cassandra.Tests.csproj", "{C6A2B99E-BFD5-4510-83D7-A8844142F27D}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ClickHouse.Tests", "tests\Testcontainers.ClickHouse.Tests\Testcontainers.ClickHouse.Tests.csproj", "{9D0A0B32-4921-400C-99CB-8650677E3E44}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.CockroachDb.Tests", "tests\Testcontainers.CockroachDb.Tests\Testcontainers.CockroachDb.Tests.csproj", "{685E6D9A-B05E-41D9-A08E-5F3CA7733F7D}"
@@ -250,6 +254,10 @@ Global
{302EC1E0-AE75-4E99-A6BF-524F35338BC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{302EC1E0-AE75-4E99-A6BF-524F35338BC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{302EC1E0-AE75-4E99-A6BF-524F35338BC8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8495D757-5FD7-491C-B941-9D43B3DCF3C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8495D757-5FD7-491C-B941-9D43B3DCF3C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8495D757-5FD7-491C-B941-9D43B3DCF3C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8495D757-5FD7-491C-B941-9D43B3DCF3C0}.Release|Any CPU.Build.0 = Release|Any CPU
{B061A78E-536E-4CA1-8401-234D5FBFBAB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B061A78E-536E-4CA1-8401-234D5FBFBAB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B061A78E-536E-4CA1-8401-234D5FBFBAB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -450,6 +458,10 @@ Global
{2E7B92E3-8526-4706-90F3-00F0F5C47C37}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2E7B92E3-8526-4706-90F3-00F0F5C47C37}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2E7B92E3-8526-4706-90F3-00F0F5C47C37}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C6A2B99E-BFD5-4510-83D7-A8844142F27D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C6A2B99E-BFD5-4510-83D7-A8844142F27D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C6A2B99E-BFD5-4510-83D7-A8844142F27D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C6A2B99E-BFD5-4510-83D7-A8844142F27D}.Release|Any CPU.Build.0 = Release|Any CPU
{9D0A0B32-4921-400C-99CB-8650677E3E44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9D0A0B32-4921-400C-99CB-8650677E3E44}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9D0A0B32-4921-400C-99CB-8650677E3E44}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -653,6 +665,7 @@ Global
{3F2E254F-C203-43FD-A078-DC3E2CBC0F9F} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{302EC1E0-AE75-4E99-A6BF-524F35338BC8} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
+ {8495D757-5FD7-491C-B941-9D43B3DCF3C0} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{B061A78E-536E-4CA1-8401-234D5FBFBAB7} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{8D9871C6-5A39-4F0B-A15A-E87D34F3EA73} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{51ED33B9-B688-401E-85F2-329D3C935BD1} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
@@ -703,6 +716,7 @@ Global
{B272FDDE-5E01-425D-B9E1-10FF883DDAAA} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{03E60673-078A-4508-99AD-8537CE6F78F1} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{2E7B92E3-8526-4706-90F3-00F0F5C47C37} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
+ {C6A2B99E-BFD5-4510-83D7-A8844142F27D} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{9D0A0B32-4921-400C-99CB-8650677E3E44} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{685E6D9A-B05E-41D9-A08E-5F3CA7733F7D} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{2478673C-B063-469D-ABD1-0C3E0A25541B} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
diff --git a/docs/modules/index.md b/docs/modules/index.md
index 6e6681edb..449deb312 100644
--- a/docs/modules/index.md
+++ b/docs/modules/index.md
@@ -29,6 +29,7 @@ await moduleNameContainer.StartAsync();
| Azurite | `mcr.microsoft.com/azure-storage/azurite:3.24.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.Azurite) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Azurite) |
| BigQuery | `ghcr.io/goccy/bigquery-emulator:0.4` | [NuGet](https://www.nuget.org/packages/Testcontainers.BigQuery) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.BigQuery) |
| Bigtable | `gcr.io/google.com/cloudsdktool/google-cloud-cli:446.0.1-emulators` | [NuGet](https://www.nuget.org/packages/Testcontainers.Bigtable) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Bigtable) |
+| Cassandra | `library/cassandra:5.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.Cassandra) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Cassandra) |
| ClickHouse | `clickhouse/clickhouse-server:23.6-alpine` | [NuGet](https://www.nuget.org/packages/Testcontainers.ClickHouse) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.ClickHouse) |
| CockroachDB | `cockroachdb:23.1.13` | [NuGet](https://www.nuget.org/packages/Testcontainers.CockroachDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.CockroachDb) |
| Consul | `consul:1.15` | [NuGet](https://www.nuget.org/packages/Testcontainers.Consul) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Consul) |
diff --git a/src/Testcontainers.Cassandra/.editorconfig b/src/Testcontainers.Cassandra/.editorconfig
new file mode 100644
index 000000000..6f066619d
--- /dev/null
+++ b/src/Testcontainers.Cassandra/.editorconfig
@@ -0,0 +1 @@
+root = true
\ No newline at end of file
diff --git a/src/Testcontainers.Cassandra/CassandraBuilder.cs b/src/Testcontainers.Cassandra/CassandraBuilder.cs
new file mode 100644
index 000000000..61a6f82b4
--- /dev/null
+++ b/src/Testcontainers.Cassandra/CassandraBuilder.cs
@@ -0,0 +1,74 @@
+namespace Testcontainers.Cassandra;
+
+///
+[PublicAPI]
+public sealed class CassandraBuilder : ContainerBuilder
+{
+ public const string CassandraImage = "cassandra:5.0";
+
+ public const ushort CqlPort = 9042;
+
+ public const string DefaultDatacenterName = "dc1";
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CassandraBuilder()
+ : this(new CassandraConfiguration())
+ {
+ DockerResourceConfiguration = Init().DockerResourceConfiguration;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Docker resource configuration.
+ private CassandraBuilder(CassandraConfiguration resourceConfiguration)
+ : base(resourceConfiguration)
+ {
+ DockerResourceConfiguration = resourceConfiguration;
+ }
+
+ ///
+ protected override CassandraConfiguration DockerResourceConfiguration { get; }
+
+ ///
+ public override CassandraContainer Build()
+ {
+ Validate();
+ return new CassandraContainer(DockerResourceConfiguration);
+ }
+
+ ///
+ protected override CassandraBuilder Init()
+ {
+ return base.Init()
+ .WithImage(CassandraImage)
+ .WithPortBinding(CqlPort, true)
+ .WithEnvironment("JVM_OPTS", "-Dcassandra.skip_wait_for_gossip_to_settle=0 -Dcassandra.initial_token=0")
+ .WithEnvironment("HEAP_NEWSIZE", "128M")
+ .WithEnvironment("MAX_HEAP_SIZE", "1024M")
+ .WithEnvironment("CASSANDRA_SNITCH", "GossipingPropertyFileSnitch")
+ .WithEnvironment("CASSANDRA_ENDPOINT_SNITCH", "GossipingPropertyFileSnitch")
+ .WithEnvironment("CASSANDRA_DC", DefaultDatacenterName)
+ .WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("Startup complete"));
+ }
+
+ ///
+ protected override CassandraBuilder Clone(IResourceConfiguration resourceConfiguration)
+ {
+ return Merge(DockerResourceConfiguration, new CassandraConfiguration(resourceConfiguration));
+ }
+
+ ///
+ protected override CassandraBuilder Clone(IContainerConfiguration resourceConfiguration)
+ {
+ return Merge(DockerResourceConfiguration, new CassandraConfiguration(resourceConfiguration));
+ }
+
+ ///
+ protected override CassandraBuilder Merge(CassandraConfiguration oldValue, CassandraConfiguration newValue)
+ {
+ return new CassandraBuilder(new CassandraConfiguration(oldValue, newValue));
+ }
+}
\ No newline at end of file
diff --git a/src/Testcontainers.Cassandra/CassandraConfiguration.cs b/src/Testcontainers.Cassandra/CassandraConfiguration.cs
new file mode 100644
index 000000000..a1a02c62b
--- /dev/null
+++ b/src/Testcontainers.Cassandra/CassandraConfiguration.cs
@@ -0,0 +1,53 @@
+namespace Testcontainers.Cassandra;
+
+///
+[PublicAPI]
+public sealed class CassandraConfiguration : ContainerConfiguration
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CassandraConfiguration()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Docker resource configuration.
+ public CassandraConfiguration(IResourceConfiguration resourceConfiguration)
+ : base(resourceConfiguration)
+ {
+ // Passes the configuration upwards to the base implementations to create an updated immutable copy.
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Docker resource configuration.
+ public CassandraConfiguration(IContainerConfiguration resourceConfiguration)
+ : base(resourceConfiguration)
+ {
+ // Passes the configuration upwards to the base implementations to create an updated immutable copy.
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Docker resource configuration.
+ public CassandraConfiguration(CassandraConfiguration resourceConfiguration)
+ : this(new CassandraConfiguration(), resourceConfiguration)
+ {
+ // Passes the configuration upwards to the base implementations to create an updated immutable copy.
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The old Docker resource configuration.
+ /// The new Docker resource configuration.
+ public CassandraConfiguration(CassandraConfiguration oldValue, CassandraConfiguration newValue)
+ : base(oldValue, newValue)
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/Testcontainers.Cassandra/CassandraContainer.cs b/src/Testcontainers.Cassandra/CassandraContainer.cs
new file mode 100644
index 000000000..d9fcd7557
--- /dev/null
+++ b/src/Testcontainers.Cassandra/CassandraContainer.cs
@@ -0,0 +1,41 @@
+namespace Testcontainers.Cassandra;
+
+///
+[PublicAPI]
+public sealed class CassandraContainer : DockerContainer, IDatabaseContainer
+{
+ ///
+ public CassandraContainer(CassandraConfiguration configuration)
+ : base(configuration)
+ {
+ }
+
+ ///
+ /// Gets the Cassandra connection string.
+ ///
+ /// The Cassandra connection string.
+ public string GetConnectionString()
+ {
+ var properties = new Dictionary();
+ properties.Add("Contact Points", Hostname);
+ properties.Add("Port", GetMappedPublicPort(CassandraBuilder.CqlPort).ToString());
+ return string.Join(";", properties.Select(property => string.Join("=", property.Key, property.Value)));
+ }
+
+ ///
+ /// Executes the CQL script in the Cassandra container.
+ ///
+ /// The content of the CQL script to execute.
+ /// Cancellation token.
+ /// Task that completes when the CQL script has been executed.
+ public async Task 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, Unix.FileMode644, ct)
+ .ConfigureAwait(false);
+
+ return await ExecAsync(new[] { "cqlsh", "--file", scriptFilePath }, ct)
+ .ConfigureAwait(false);
+ }
+}
\ No newline at end of file
diff --git a/src/Testcontainers.Cassandra/Testcontainers.Cassandra.csproj b/src/Testcontainers.Cassandra/Testcontainers.Cassandra.csproj
new file mode 100644
index 000000000..906f34018
--- /dev/null
+++ b/src/Testcontainers.Cassandra/Testcontainers.Cassandra.csproj
@@ -0,0 +1,12 @@
+
+
+ net8.0;net9.0;netstandard2.0;netstandard2.1
+ latest
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Testcontainers.Cassandra/Usings.cs b/src/Testcontainers.Cassandra/Usings.cs
new file mode 100644
index 000000000..191eb3bce
--- /dev/null
+++ b/src/Testcontainers.Cassandra/Usings.cs
@@ -0,0 +1,12 @@
+global using System;
+global using System.IO;
+global using System.Text;
+global using System.Linq;
+global using System.Threading;
+global using System.Threading.Tasks;
+global using System.Collections.Generic;
+global using Docker.DotNet.Models;
+global using DotNet.Testcontainers.Builders;
+global using DotNet.Testcontainers.Configurations;
+global using DotNet.Testcontainers.Containers;
+global using JetBrains.Annotations;
\ No newline at end of file
diff --git a/tests/Testcontainers.Cassandra.Tests/.editorconfig b/tests/Testcontainers.Cassandra.Tests/.editorconfig
new file mode 100644
index 000000000..6f066619d
--- /dev/null
+++ b/tests/Testcontainers.Cassandra.Tests/.editorconfig
@@ -0,0 +1 @@
+root = true
\ No newline at end of file
diff --git a/tests/Testcontainers.Cassandra.Tests/CassandraContainerTest.cs b/tests/Testcontainers.Cassandra.Tests/CassandraContainerTest.cs
new file mode 100644
index 000000000..0af198beb
--- /dev/null
+++ b/tests/Testcontainers.Cassandra.Tests/CassandraContainerTest.cs
@@ -0,0 +1,69 @@
+namespace Testcontainers.Cassandra;
+
+public sealed class CassandraContainerTest : IAsyncLifetime
+{
+ private readonly CassandraContainer _cassandraContainer = new CassandraBuilder().Build();
+
+ public Task InitializeAsync()
+ {
+ return _cassandraContainer.StartAsync();
+ }
+
+ public Task DisposeAsync()
+ {
+ return _cassandraContainer.DisposeAsync().AsTask();
+ }
+
+ [Fact]
+ [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
+ public void ConnectionStateReturnsOpen()
+ {
+ // Given
+ using DbConnection connection = new CqlConnection(_cassandraContainer.GetConnectionString());
+
+ // When
+ connection.Open();
+
+ // Then
+ Assert.Equal(ConnectionState.Open, connection.State);
+ }
+
+ [Fact]
+ [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
+ public void ExecuteCqlStatementReturnsExpectedResult()
+ {
+ // Given
+ const string selectFromSystemLocalStatement = "SELECT * FROM system.local WHERE key = ?;";
+
+ using var cluster = Cluster.Builder().WithConnectionString(_cassandraContainer.GetConnectionString()).Build();
+
+ // When
+ using var session = cluster.Connect();
+
+ var preparedStatement = session.Prepare(selectFromSystemLocalStatement);
+ var boundStatement = preparedStatement.Bind("local");
+ using var rowSet = session.Execute(boundStatement);
+ var rows = rowSet.GetRows().ToImmutableList();
+
+ // Then
+ Assert.True(rowSet.IsFullyFetched);
+ Assert.Single(rows);
+ Assert.Equal("COMPLETED", rows[0]["bootstrapped"]);
+ }
+
+ [Fact]
+ [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
+ public async Task ExecScriptAsyncReturnsSuccess()
+ {
+ // Given
+ const string selectFromSystemLocalStatement = "SELECT * FROM system.local;";
+
+ // When
+ var execResult = await _cassandraContainer.ExecScriptAsync(selectFromSystemLocalStatement)
+ .ConfigureAwait(true);
+
+ // Then
+ Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr);
+ Assert.Empty(execResult.Stderr);
+ }
+}
\ No newline at end of file
diff --git a/tests/Testcontainers.Cassandra.Tests/Testcontainers.Cassandra.Tests.csproj b/tests/Testcontainers.Cassandra.Tests/Testcontainers.Cassandra.Tests.csproj
new file mode 100644
index 000000000..467cd3d93
--- /dev/null
+++ b/tests/Testcontainers.Cassandra.Tests/Testcontainers.Cassandra.Tests.csproj
@@ -0,0 +1,18 @@
+
+
+ net9.0
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/Testcontainers.Cassandra.Tests/Usings.cs b/tests/Testcontainers.Cassandra.Tests/Usings.cs
new file mode 100644
index 000000000..cb1d4b9d6
--- /dev/null
+++ b/tests/Testcontainers.Cassandra.Tests/Usings.cs
@@ -0,0 +1,8 @@
+global using System.Collections.Immutable;
+global using System.Data;
+global using System.Data.Common;
+global using System.Threading.Tasks;
+global using Cassandra;
+global using Cassandra.Data;
+global using DotNet.Testcontainers.Commons;
+global using Xunit;
\ No newline at end of file