diff --git a/src/Dapr.Testcontainers/Common/Options/DaprRuntimeOptions.cs b/src/Dapr.Testcontainers/Common/Options/DaprRuntimeOptions.cs index 973654f86..e533feb5c 100644 --- a/src/Dapr.Testcontainers/Common/Options/DaprRuntimeOptions.cs +++ b/src/Dapr.Testcontainers/Common/Options/DaprRuntimeOptions.cs @@ -12,6 +12,7 @@ // ------------------------------------------------------------------------ using System; +using System.Collections.Generic; namespace Dapr.Testcontainers.Common.Options; @@ -21,6 +22,7 @@ namespace Dapr.Testcontainers.Common.Options; public sealed record DaprRuntimeOptions { private const string DEFAULT_VERSION_ENVVAR_NAME = "DAPR_RUNTIME_VERSION"; + private readonly Dictionary environmentVariables = new(StringComparer.Ordinal); private static readonly string[] CiEnvironmentSignals = [ "CI", @@ -73,6 +75,11 @@ public DaprRuntimeOptions(string version = "latest") /// The Dapr API token used to secure communications with the sidecar. /// public string? DaprApiToken { get; private set; } + + /// + /// Environment variables to set on the Dapr runtime container. + /// + public IReadOnlyDictionary EnvironmentVariables => environmentVariables; /// /// The level of Dapr logs to show. @@ -149,6 +156,20 @@ public DaprRuntimeOptions WithDaprApiToken(string daprApiToken) return this; } + /// + /// Sets an environment variable on the Dapr runtime container. + /// + /// The environment variable name. + /// The environment variable value. + public DaprRuntimeOptions WithEnvironmentVariable(string name, string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + ArgumentNullException.ThrowIfNull(value); + + environmentVariables[name] = value; + return this; + } + /// /// Enables container log capture to files. /// diff --git a/src/Dapr.Testcontainers/Containers/Dapr/DaprdContainer.cs b/src/Dapr.Testcontainers/Containers/Dapr/DaprdContainer.cs index 7fd46cd41..511c7caa9 100644 --- a/src/Dapr.Testcontainers/Containers/Dapr/DaprdContainer.cs +++ b/src/Dapr.Testcontainers/Containers/Dapr/DaprdContainer.cs @@ -167,6 +167,11 @@ public DaprdContainer( containerBuilder = containerBuilder.WithOutputConsumer(_logAttachment.OutputConsumer); } + foreach (var (name, value) in options.EnvironmentVariables) + { + containerBuilder = containerBuilder.WithEnvironment(name, value); + } + // Put the API token in an envvar so it can be picked up by the Dapr runtime at startup if (!string.IsNullOrWhiteSpace(options.DaprApiToken)) { diff --git a/test/Dapr.IntegrationTest.DistributedLock/DistributedLockTests.cs b/test/Dapr.IntegrationTest.DistributedLock/DistributedLockTests.cs index abc502bbd..257a34a6a 100644 --- a/test/Dapr.IntegrationTest.DistributedLock/DistributedLockTests.cs +++ b/test/Dapr.IntegrationTest.DistributedLock/DistributedLockTests.cs @@ -2,7 +2,9 @@ using Dapr.DistributedLock.Extensions; using Dapr.DistributedLock.Models; using Dapr.Testcontainers.Common; +using Dapr.Testcontainers.Common.Options; using Dapr.Testcontainers.Harnesses; +using Grpc.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -16,11 +18,17 @@ public async Task ShouldAcquireAndReleaseLock() var componentsDir = TestDirectoryManager.CreateTestDirectory("distributedlock-components"); var resourceId = $"resource-{Guid.NewGuid():N}"; var owner = $"owner-{Guid.NewGuid():N}"; + var daprApiToken = $"distributed-lock-token-{Guid.NewGuid():N}"; + var options = new DaprRuntimeOptions() + .WithEnvironmentVariable("DAPR_API_TOKEN", daprApiToken); await using var environment = await DaprTestEnvironment.CreateWithPooledNetworkAsync(cancellationToken: TestContext.Current.CancellationToken); await environment.StartAsync(TestContext.Current.CancellationToken); - var harness = new DaprHarnessBuilder(componentsDir).BuildDistributedLock(); + var harness = new DaprHarnessBuilder(componentsDir) + .WithEnvironment(environment) + .WithOptions(options) + .BuildDistributedLock(); await using var testApp = await DaprHarnessBuilder.ForHarness(harness) .ConfigureServices(builder => { @@ -30,6 +38,7 @@ public async Task ShouldAcquireAndReleaseLock() var grpcEndpoint = config["DAPR_GRPC_ENDPOINT"]; if (!string.IsNullOrEmpty(grpcEndpoint)) clientBuilder.UseGrpcEndpoint(grpcEndpoint); + clientBuilder.UseDaprApiToken(daprApiToken); }); }) .BuildAndStartAsync(); @@ -37,6 +46,19 @@ public async Task ShouldAcquireAndReleaseLock() const string componentName = DistributedLockHarness.DistributedLockComponentName; Assert.NotNull(componentName); + using var unauthenticatedClient = new DaprDistributedLockBuilder() + .UseGrpcEndpoint($"http://127.0.0.1:{harness.DaprGrpcPort}") + .Build(); + var unauthenticatedException = await Assert.ThrowsAsync( + () => unauthenticatedClient.TryLockAsync( + componentName, + $"unauthenticated-{resourceId}", + owner, + expiryInSeconds: 10, + cancellationToken: TestContext.Current.CancellationToken)); + var rpcException = Assert.IsType(unauthenticatedException.InnerException); + Assert.Equal(StatusCode.Unauthenticated, rpcException.StatusCode); + using var scope = testApp.CreateScope(); var client = scope.ServiceProvider.GetRequiredService(); diff --git a/test/Dapr.Testcontainers.Test/Common/Options/DaprRuntimeOptionsTests.cs b/test/Dapr.Testcontainers.Test/Common/Options/DaprRuntimeOptionsTests.cs index 2f1d0610b..63f32903d 100644 --- a/test/Dapr.Testcontainers.Test/Common/Options/DaprRuntimeOptionsTests.cs +++ b/test/Dapr.Testcontainers.Test/Common/Options/DaprRuntimeOptionsTests.cs @@ -50,6 +50,27 @@ public void ShouldEnableContainerLogsForCiDebugLogging() } } + [Fact] + public void ShouldSetEnvironmentVariable() + { + var options = new DaprRuntimeOptions() + .WithEnvironmentVariable("REDIS_HOST", "redis:6379") + .WithEnvironmentVariable("REDIS_PASSWORD", "password"); + + Assert.Equal("redis:6379", options.EnvironmentVariables["REDIS_HOST"]); + Assert.Equal("password", options.EnvironmentVariables["REDIS_PASSWORD"]); + } + + [Fact] + public void ShouldOverwriteEnvironmentVariable() + { + var options = new DaprRuntimeOptions() + .WithEnvironmentVariable("REDIS_HOST", "redis:6379") + .WithEnvironmentVariable("REDIS_HOST", "redis:6380"); + + Assert.Equal("redis:6380", options.EnvironmentVariables["REDIS_HOST"]); + } + public void Dispose() { // Clear this variable