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
5 changes: 5 additions & 0 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ name: Continuous Integration & Delivery

on:
push:
branches: [develop, main, bugfix/*, feature/*]
paths-ignore: ["docs/**", "examples/**"]
pull_request:
branches: [develop, main]
paths-ignore: ["docs/**", "examples/**"]
branches: [ develop, main, bugfix/*, feature/* ]
pull_request:
branches: [ develop, main ]
Comment on lines +7 to 12
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove duplicate pull_request event trigger and consolidate overlapping branches keys.

YAML does not support duplicate keys at the same nesting level. The current configuration has two pull_request triggers (lines 7–9 and lines 11–12), and the second one will override the first. Additionally, within the first pull_request block, there are two conflicting branches keys (lines 8 and 10), where line 10 will override line 8.

This means the paths-ignore filter and the feature/* branch pattern from lines 5–6 will be lost for pull_request events. The workflow will fall back to the legacy configuration on lines 11–12.

Apply this diff to remove the duplicate trigger and consolidate the configuration:

  pull_request:
    branches: [develop, main]
    paths-ignore: ["docs/**", "examples/**"]
-    branches: [ develop, main, bugfix/*, feature/* ]
-  pull_request:
-    branches: [ develop, main ]
🧰 Tools
🪛 actionlint (1.7.8)

10-10: key "branches" is duplicated in "pull_request" section. previously defined at line:8,col:5

(syntax-check)


11-11: key "pull_request" is duplicated in "on" section. previously defined at line:7,col:3

(syntax-check)

🪛 YAMLlint (1.37.1)

[error] 10-10: duplication of key "branches" in mapping

(key-duplicates)


[error] 11-11: duplication of key "pull_request" in mapping

(key-duplicates)

🤖 Prompt for AI Agents
In .github/workflows/cicd.yml around lines 7 to 12, remove the duplicate
pull_request block and consolidate the configuration into a single pull_request
trigger that includes the full branches list and the paths-ignore filter;
specifically, keep one pull_request key that lists branches: [develop, main,
bugfix/*, feature/*] and retain paths-ignore: ["docs/**", "examples/**"], and
delete the second redundant pull_request block so no keys are overridden.

Expand Down
5 changes: 5 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.6.1"/>
<PackageVersion Include="Docker.DotNet.Enhanced.X509" Version="3.128.3"/>
<PackageVersion Include="Docker.DotNet.Enhanced" Version="3.128.3"/>
<PackageVersion Include="Grpc.Core" Version="2.46.6" />
<PackageVersion Include="Docker.DotNet.Enhanced.X509" Version="3.129.0"/>
<PackageVersion Include="Docker.DotNet.Enhanced" Version="3.129.0"/>
Comment on lines +8 to 12
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Resolve duplicate central package versions.

Adding the 3.128.3 entries for Docker.DotNet.Enhanced(.X509) alongside the existing 3.129.0 definitions causes MSBuild to choke (PackageVersion items must be unique). Drop the new 3.128.3 lines (or bump the module to 3.129.0) so only one version remains.

-        <PackageVersion Include="Docker.DotNet.Enhanced.X509" Version="3.128.3"/>
-        <PackageVersion Include="Docker.DotNet.Enhanced" Version="3.128.3"/>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<PackageVersion Include="Docker.DotNet.Enhanced.X509" Version="3.128.3"/>
<PackageVersion Include="Docker.DotNet.Enhanced" Version="3.128.3"/>
<PackageVersion Include="Grpc.Core" Version="2.46.6" />
<PackageVersion Include="Docker.DotNet.Enhanced.X509" Version="3.129.0"/>
<PackageVersion Include="Docker.DotNet.Enhanced" Version="3.129.0"/>
<PackageVersion Include="Grpc.Core" Version="2.46.6" />
<PackageVersion Include="Docker.DotNet.Enhanced.X509" Version="3.129.0"/>
<PackageVersion Include="Docker.DotNet.Enhanced" Version="3.129.0"/>
🤖 Prompt for AI Agents
In Directory.Packages.props around lines 8 to 12 there are duplicate
PackageVersion entries for Docker.DotNet.Enhanced and
Docker.DotNet.Enhanced.X509 (both 3.128.3 and 3.129.0) which breaks MSBuild;
remove the 3.128.3 entries or update any dependent modules to use 3.129.0 so
each PackageVersion Include appears only once, leaving a single canonical
version for both Docker.DotNet.Enhanced and Docker.DotNet.Enhanced.X509.

<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0"/>
Expand Down Expand Up @@ -61,6 +64,8 @@
<PackageVersion Include="Google.Cloud.Firestore" Version="3.1.0"/>
<PackageVersion Include="Google.Cloud.PubSub.V1" Version="3.5.0"/>
<PackageVersion Include="Google.Cloud.Storage.V1" Version="4.6.0"/>
<PackageVersion Include="Grpc.HealthCheck" Version="2.60.0"/>
<PackageVersion Include="Grpc.Net.Client" Version="2.60.0"/>
<PackageVersion Include="InfluxDB.Client" Version="4.18.0"/>
<PackageVersion Include="JanusGraph.Net" Version="1.0.0"/>
<PackageVersion Include="Keycloak.Net.Core" Version="1.0.20"/>
Expand Down
18 changes: 18 additions & 0 deletions Testcontainers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,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.SpiceDB", "src\Testcontainers.SpiceDB\Testcontainers.SpiceDB.csproj", "{64B27088-14DC-4CA2-B24E-5D0D5BA14355}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.SpiceDB.Tests", "tests\Testcontainers.SpiceDB.Tests\Testcontainers.SpiceDB.Tests.csproj", "{21D155EB-A843-4D4D-84E1-5C913217BE89}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -806,6 +810,18 @@ 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
{64B27088-14DC-4CA2-B24E-5D0D5BA14355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{64B27088-14DC-4CA2-B24E-5D0D5BA14355}.Debug|Any CPU.Build.0 = Debug|Any CPU
{64B27088-14DC-4CA2-B24E-5D0D5BA14355}.Release|Any CPU.ActiveCfg = Release|Any CPU
{64B27088-14DC-4CA2-B24E-5D0D5BA14355}.Release|Any CPU.Build.0 = Release|Any CPU
{A9F554E5-9183-4F8A-B226-58DD46BB2060}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A9F554E5-9183-4F8A-B226-58DD46BB2060}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9F554E5-9183-4F8A-B226-58DD46BB2060}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A9F554E5-9183-4F8A-B226-58DD46BB2060}.Release|Any CPU.Build.0 = Release|Any CPU
Comment on lines +817 to +820
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: GUID mismatch detected.

Lines 817-820 reference GUID {A9F554E5-9183-4F8A-B226-58DD46BB2060}, but the project declaration on Line 283 uses {64B27088-14DC-4CA2-B24E-5D0D5BA14355}. This mismatch will cause build configuration issues.

-		{A9F554E5-9183-4F8A-B226-58DD46BB2060}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{A9F554E5-9183-4F8A-B226-58DD46BB2060}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{A9F554E5-9183-4F8A-B226-58DD46BB2060}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{A9F554E5-9183-4F8A-B226-58DD46BB2060}.Release|Any CPU.Build.0 = Release|Any CPU
+		{64B27088-14DC-4CA2-B24E-5D0D5BA14355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{64B27088-14DC-4CA2-B24E-5D0D5BA14355}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{64B27088-14DC-4CA2-B24E-5D0D5BA14355}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{64B27088-14DC-4CA2-B24E-5D0D5BA14355}.Release|Any CPU.Build.0 = Release|Any CPU

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In Testcontainers.sln around lines 817 to 820, the project configuration entries
use GUID {A9F554E5-9183-4F8A-B226-58DD46BB2060} which does not match the
project's GUID {64B27088-14DC-4CA2-B24E-5D0D5BA14355} declared at line 283;
update the four configuration lines (Debug|Any CPU.ActiveCfg, Debug|Any
CPU.Build.0, Release|Any CPU.ActiveCfg, Release|Any CPU.Build.0) to use the
correct GUID {64B27088-14DC-4CA2-B24E-5D0D5BA14355} (or if the project GUID at
line 283 is wrong, update that one instead so they match), ensure all
occurrences across the .sln are consistent, then save and verify the solution
loads/builds successfully.

{21D155EB-A843-4D4D-84E1-5C913217BE89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{21D155EB-A843-4D4D-84E1-5C913217BE89}.Debug|Any CPU.Build.0 = Debug|Any CPU
{21D155EB-A843-4D4D-84E1-5C913217BE89}.Release|Any CPU.ActiveCfg = Release|Any CPU
{21D155EB-A843-4D4D-84E1-5C913217BE89}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -942,5 +958,7 @@ 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}
{64B27088-14DC-4CA2-B24E-5D0D5BA14355} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{21D155EB-A843-4D4D-84E1-5C913217BE89} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
EndGlobalSection
EndGlobal
3 changes: 2 additions & 1 deletion docs/modules/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ await moduleNameContainer.StartAsync();
We will add module-specific documentations soon.

| Module | Image | NuGet | Source |
|-------------------|---------------------------------------------------------------------|----------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------|
| ----------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| ActiveMQ Artemis | `apache/activemq-artemis:2.31.2` | [NuGet](https://www.nuget.org/packages/Testcontainers.ActiveMq) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.ActiveMq) |
| ArangoDB | `arangodb:3.11.5` | [NuGet](https://www.nuget.org/packages/Testcontainers.ArangoDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.ArangoDb) |
| Azure Cosmos DB | `mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest` | [NuGet](https://www.nuget.org/packages/Testcontainers.CosmosDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.CosmosDb) |
Expand Down Expand Up @@ -77,6 +77,7 @@ await moduleNameContainer.StartAsync();
| Typesense | `typesense/typesense:28.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.Typesense) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Typesense) |
| Weaviate | `semitechnologies/weaviate:1.26.14` | [NuGet](https://www.nuget.org/packages/Testcontainers.Weaviate) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Weaviate) |
| WebDriver | `selenium/standalone-chrome:110.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.WebDriver) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.WebDriver) |
| SpiceDB | `authzed/spicedb:v1.45.1` | [NuGet](https://www.nuget.org/packages/Testcontainers.SpiceDB) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.SpiceDB) |
| WireMock | - | [NuGet](https://www.nuget.org/packages/WireMock.Net.Testcontainers) | [Source](https://github.com/wiremock/WireMock.Net) |

## Implement a module
Expand Down
56 changes: 56 additions & 0 deletions docs/modules/spicedb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# SpiceDB

[SpiceDB](https://authzed.com/spicedb/) is an open-source, Google Zanzibar-inspired permissions database that provides a centralized service to store, compute, and validate application permissions. It enables fine-grained authorization at scale with a flexible relationship-based permission model.

Add the following dependency to your project file:

```shell title="NuGet"
dotnet add package Testcontainers.SpiceDB
```

You can start a SpiceDB container instance from any .NET application. This example uses xUnit.net's `IAsyncLifetime` interface to manage the lifecycle of the container. The container is started in the `InitializeAsync` method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the `DisposeAsync` method.

=== "Usage Example"

````csharp
public sealed class SpiceDBContainerTest : IAsyncLifetime
{
private readonly SpiceDBContainer \_spicedbContainer = new SpiceDBBuilder().Build();

public Task InitializeAsync()
{
return _spicedbContainer.StartAsync();
}

public Task DisposeAsync()
{
return _spicedbContainer.DisposeAsync().AsTask();
}

[Fact]
public async Task SpiceDBContainer_IsRunning_ReturnsTrue()
{
// Given
var containerState = await _spicedbContainer.GetStateAsync();

// ...
}
}
```

Comment on lines +13 to +40
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix the tabbed code block fencing.

The === "Usage Example" section opens with ````csharp but closes with an indented ```; MkDocs will treat this as mismatched fences, so the code sample renders incorrectly. Indent the block and use matching triple backticks inside the tab (no need for the leading backslash on _). Suggested fix:

-=== "Usage Example"
-
-````csharp
-public sealed class SpiceDBContainerTest : IAsyncLifetime
-{
-private readonly SpiceDBContainer \_spicedbContainer = new SpiceDBBuilder().Build();
-
-        public Task InitializeAsync()
-        {
-            return _spicedbContainer.StartAsync();
-        }
-
-        public Task DisposeAsync()
-        {
-            return _spicedbContainer.DisposeAsync().AsTask();
-        }
-
-        [Fact]
-        public async Task SpiceDBContainer_IsRunning_ReturnsTrue()
-        {
-            // Given
-            var containerState = await _spicedbContainer.GetStateAsync();
-
-            // ...
-        }
-    }
-    ```
+=== "Usage Example"
+
+    ```csharp
+    public sealed class SpiceDBContainerTest : IAsyncLifetime
+    {
+        private readonly SpiceDBContainer _spicedbContainer = new SpiceDBBuilder().Build();
+
+        public Task InitializeAsync()
+        {
+            return _spicedbContainer.StartAsync();
+        }
+
+        public Task DisposeAsync()
+        {
+            return _spicedbContainer.DisposeAsync().AsTask();
+        }
+
+        [Fact]
+        public async Task SpiceDBContainer_IsRunning_ReturnsTrue()
+        {
+            // Given
+            var containerState = await _spicedbContainer.GetStateAsync();
+
+            // ...
+        }
+    }
+    ```
🤖 Prompt for AI Agents
In docs/modules/spicedb.md around lines 13 to 40, the tabbed "Usage Example"
block opens with ````csharp but closes with an indented ``` and contains an
escaped underscore, causing mismatched fences and wrong rendering; fix by
replacing the current block with a properly indented tab content: start the tab
with === "Usage Example", indent the entire code block by four spaces and use
matching triple backticks (```csharp ... ```), remove the backslash before
_spicedbContainer so the identifier is _spicedbContainer, and ensure the opening
and closing backticks align at the same indentation level inside the tab.

The test example uses the following NuGet dependencies:

=== "Package References"
`xml
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
<PackageReference Include="Testcontainers.SpiceDB" />
</ItemGroup>
`

To execute the tests, use the command `dotnet test` from a terminal.

--8<-- "docs/modules/\_call_out_test_projects.txt"
````
1 change: 1 addition & 0 deletions src/Testcontainers.SpiceDB/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove nested root = true.

Setting root = true in a subdirectory severs this project from the repo-wide .editorconfig, so all parent formatting/analyzer rules stop applying to the SpiceDB sources. Please drop this file (or at least the directive) so the shared configuration continues to flow down.

🤖 Prompt for AI Agents
In src/Testcontainers.SpiceDB/.editorconfig around line 1, the file sets "root =
true" which prevents the repo-wide .editorconfig from being inherited; remove
that directive (or delete this .editorconfig entirely) so the parent/shared
formatting and analyzer rules continue to apply to the SpiceDB sources, then
save and commit the change.

71 changes: 71 additions & 0 deletions src/Testcontainers.SpiceDB/SpiceDBBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
namespace Testcontainers.SpiceDB;

/// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" />
[PublicAPI]
public sealed class SpiceDBBuilder : ContainerBuilder<SpiceDBBuilder, SpiceDBContainer, SpiceDBConfiguration>
{
public const string SpiceDBImage = "authzed/spicedb:v1.45.1";

public const ushort SpiceDBgRPCPort = 50051;

public const ushort SpiceDBgHTTPPort = 8443;


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

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

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

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

/// <inheritdoc />
protected override SpiceDBBuilder Init()
{
return base.Init()
.WithImage(SpiceDBImage)
.WithPortBinding(SpiceDBgRPCPort, true)
.WithPortBinding(SpiceDBgHTTPPort, true)
.WithCommand("serve", $"--grpc-preshared-key={DockerResourceConfiguration.GrpcPresharedKey}", $"--datastore-engine={DockerResourceConfiguration.DatastoreEngine}", $"--log-level=info")
.WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("grpc server started serving"));
Comment on lines +46 to +51
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

TLS flag in configuration is ignored, breaking HTTPS clients

SpiceDBConfiguration.TslEnabled flips the connection string to https:// (see SpiceDBContainer.GetGrpcConnectionString), but this start command never enables TLS on the server. SpiceDB still logs insecure=true, so any client honoring the HTTPS URI will try a TLS handshake against an insecure endpoint and fail. Please either propagate TslEnabled into the command (supplying the appropriate --grpc-tls-* flags or certificates) or block the flag until proper TLS support exists; right now setting it to true is a functional regression for consumers.

}

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

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

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

/// <inheritdoc cref="ContainerConfiguration" />
[PublicAPI]
public sealed class SpiceDBConfiguration : ContainerConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="SpiceDBConfiguration" /> class.
/// </summary>
public SpiceDBConfiguration(string grpcPresharedKey = "mysecret", string datastoreEngine = "memory", bool? tslEnabled = false)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix typo: "Tsl" should be "Tls".

The parameter name tslEnabled and property name TslEnabled contain a typo. The correct abbreviation for "Transport Layer Security" is "TLS", not "TSL".

-    public SpiceDBConfiguration(string grpcPresharedKey = "mysecret", string datastoreEngine = "memory", bool? tslEnabled = false)
+    public SpiceDBConfiguration(string grpcPresharedKey = "mysecret", string datastoreEngine = "memory", bool? tlsEnabled = false)
    {
        GrpcPresharedKey = grpcPresharedKey;
        DatastoreEngine = datastoreEngine;
-        TslEnabled = tslEnabled.GetValueOrDefault(false);
+        TlsEnabled = tlsEnabled.GetValueOrDefault(false);
    }

-    public bool TslEnabled { get; set; }
+    public bool TlsEnabled { get; set; }

Note: This change will also require updates in SpiceDBContainer.cs where TslEnabled is referenced.

Also applies to: 17-17

🤖 Prompt for AI Agents
In src/Testcontainers.SpiceDB/SpiceDBConfiguration.cs around lines 10 and 17,
the boolean parameter and property are misspelled as "tslEnabled"/"TslEnabled";
rename them to "tlsEnabled"/"TlsEnabled" (update the constructor parameter,
backing field/property name, and any XML/comments) and propagate the change to
all references (e.g., update SpiceDBContainer.cs where TslEnabled is referenced)
to ensure consistency and rebuild references/usages across the project.

{
GrpcPresharedKey = grpcPresharedKey;
DatastoreEngine = datastoreEngine;
TslEnabled = tslEnabled.GetValueOrDefault(false);
}

public bool TslEnabled { get; set; }

public string GrpcPresharedKey { get; set; }

public string DatastoreEngine { get; set; }


/// <summary>
/// Initializes a new instance of the <see cref="SpiceDBConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public SpiceDBConfiguration(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="SpiceDBConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public SpiceDBConfiguration(IContainerConfiguration resourceConfiguration)
: base(resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}
Comment on lines +24 to +42
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify property initialization in copy constructors.

The constructors at lines 28-32 and 38-42 rely on the base class to handle configuration copying. Ensure that ContainerConfiguration properly preserves derived class properties, or these constructors may need to explicitly initialize the SpiceDB-specific properties.

Run the following script to check how similar modules handle this pattern:


🏁 Script executed:

#!/bin/bash
# Search for similar configuration patterns in other modules
rg -A 10 'public.*Configuration.*IResourceConfiguration' --type cs -g 'src/Testcontainers.*/Configuration.cs' | head -100

Length of output: 148


I'll help verify this review comment by examining the codebase structure and comparing patterns. Let me gather the necessary information.


---

🏁 Script executed:

```shell
#!/bin/bash
# Find all Configuration.cs files in the Testcontainers modules
fd -t f 'Configuration\.cs$' 'src/Testcontainers'

Length of output: 1408


🏁 Script executed:

#!/bin/bash
# Examine the SpiceDBConfiguration.cs file
cat 'src/Testcontainers.SpiceDB/SpiceDBConfiguration.cs'

Length of output: 2533


🏁 Script executed:

#!/bin/bash
# Find and examine the base ContainerConfiguration class
fd -t f 'ContainerConfiguration\.cs$' --type f

Length of output: 142


🏁 Script executed:

#!/bin/bash
# Look for similar configuration patterns in other Testcontainers modules
find src/Testcontainers.* -name 'Configuration.cs' -type f | head -20

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Search for how other modules define their configurations
rg 'class.*Configuration.*:.*Configuration' --type cs -A 5 -B 2 | head -150

Length of output: 13275


I need to examine the base class and compare with other similar modules to verify the concern. Let me inspect the implementations.


---

🏁 Script executed:

```shell
#!/bin/bash
# Examine the base ContainerConfiguration class
cat 'src/Testcontainers/Configurations/Containers/ContainerConfiguration.cs'

Length of output: 9313


🏁 Script executed:

#!/bin/bash
# Examine WeaviateConfiguration to see how it handles derived properties
cat 'src/Testcontainers.Weaviate/WeaviateConfiguration.cs'

Length of output: 2164


🏁 Script executed:

#!/bin/bash
# Examine RedisConfiguration for comparison
cat 'src/Testcontainers.Redis/RedisConfiguration.cs'

Length of output: 2116


🏁 Script executed:

#!/bin/bash
# Examine RavenDbConfiguration for comparison
cat 'src/Testcontainers.RavenDb/RavenDbConfiguration.cs'

Length of output: 2148


Add explicit property initialization to copy constructors to preserve SpiceDB-specific configuration.

The constructors at lines 28-32 and 38-42 rely on base class handling, but ContainerConfiguration doesn't know about TslEnabled, GrpcPresharedKey, and DatastoreEngine. When these constructors are called with an existing SpiceDBConfiguration, the derived properties are lost.

The copy constructor at lines 54-60 also needs to explicitly merge the derived properties like the base class does with BuildConfiguration.Combine().

Update all copy constructors to preserve SpiceDB-specific properties:

public SpiceDBConfiguration(SpiceDBConfiguration oldValue, SpiceDBConfiguration newValue)
    : base(oldValue, newValue)
{
    TslEnabled = newValue.TslEnabled;
    GrpcPresharedKey = newValue.GrpcPresharedKey;
    DatastoreEngine = newValue.DatastoreEngine;
}
🤖 Prompt for AI Agents
In src/Testcontainers.SpiceDB/SpiceDBConfiguration.cs around lines 24-42 (and
also update the copy constructor around lines 54-60), the derived properties
TslEnabled, GrpcPresharedKey, and DatastoreEngine are not preserved because the
base constructors don’t know about them; change the two constructors that accept
IResourceConfiguration/CreateContainerParameters and IContainerConfiguration to
detect if the provided configuration is a SpiceDBConfiguration (or accept
SpiceDBConfiguration overloads) and explicitly copy those three properties from
the incoming instance to this instance, and update the
SpiceDBConfiguration(SpiceDBConfiguration oldValue, SpiceDBConfiguration
newValue) copy constructor to assign TslEnabled, GrpcPresharedKey, and
DatastoreEngine from newValue after calling base(oldValue, newValue).


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

/// <summary>
/// Initializes a new instance of the <see cref="SpiceDBConfiguration" /> class.
/// </summary>
/// <param name="oldValue">The old Docker resource configuration.</param>
/// <param name="newValue">The new Docker resource configuration.</param>
public SpiceDBConfiguration(SpiceDBConfiguration oldValue, SpiceDBConfiguration newValue)
: base(oldValue, newValue)
{
}
Comment on lines +54 to +62
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Merge constructor must copy SpiceDB-specific properties.

The merge constructor doesn't copy the SpiceDB-specific properties (GrpcPresharedKey, DatastoreEngine, TslEnabled) from oldValue and newValue. This will cause configuration loss when the builder clones or merges configurations.

    public SpiceDBConfiguration(SpiceDBConfiguration oldValue, SpiceDBConfiguration newValue)
        : base(oldValue, newValue)
    {
+        GrpcPresharedKey = BuildConfiguration.Combine(oldValue, newValue, @this => @this.GrpcPresharedKey);
+        DatastoreEngine = BuildConfiguration.Combine(oldValue, newValue, @this => @this.DatastoreEngine);
+        TslEnabled = BuildConfiguration.Combine(oldValue, newValue, @this => @this.TslEnabled);
    }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/Testcontainers.SpiceDB/SpiceDBConfiguration.cs around lines 54 to 62, the
merge constructor currently only calls the base ctor and fails to copy
SpiceDB-specific properties; update the constructor to assign
this.GrpcPresharedKey, this.DatastoreEngine and this.TslEnabled from the
incoming newValue (falling back to oldValue if newValue is null or its property
is not set) so merged/cloned configurations preserve those fields—use null
checks/conditional operators to prefer newValue values and fallback to oldValue
values, handling boolean/nullability appropriately.

}
28 changes: 28 additions & 0 deletions src/Testcontainers.SpiceDB/SpiceDBContainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Threading;
using Health = Grpc.Health.V1.Health;

namespace Testcontainers.SpiceDB;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class SpiceDBContainer : DockerContainer
{
SpiceDBConfiguration _configuration;

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

public string GetGrpcConnectionString()
{
var scheme = _configuration.TslEnabled ? Uri.UriSchemeHttps : Uri.UriSchemeHttp;
var endpoint = new UriBuilder(scheme, Hostname, GetMappedPublicPort(SpiceDBBuilder.SpiceDBgRPCPort));
return endpoint.ToString();
}
}
17 changes: 17 additions & 0 deletions src/Testcontainers.SpiceDB/Testcontainers.SpiceDB.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0;net9.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<LangVersion>latest</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Grpc.HealthCheck" />
<PackageReference Include="JetBrains.Annotations" VersionOverride="2023.3.0" PrivateAssets="All"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Testcontainers\Testcontainers.csproj" />
</ItemGroup>

</Project>
9 changes: 9 additions & 0 deletions src/Testcontainers.SpiceDB/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
global using System;
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;
global using Grpc.Health.V1;
global using Grpc.Core;
1 change: 1 addition & 0 deletions tests/Testcontainers.SpiceDB.Tests/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Keep inheriting the repo .editorconfig.

Marking this test-project .editorconfig as root = true blocks all shared editor/analyzer settings from the rest of the repository. Please remove the directive (or the file) so the tests inherit the common rules.

🤖 Prompt for AI Agents
In tests/Testcontainers.SpiceDB.Tests/.editorconfig at line 1, the file sets
"root = true" which prevents inheriting the repository-wide .editorconfig;
remove the "root = true" directive (or delete the .editorconfig file entirely)
so this test project inherits the shared editor/analyzer settings from the repo
root, then run a quick format/lint check to confirm settings are applied.

Loading
Loading