// ------------------------------------------------------------ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. // ------------------------------------------------------------ using System; using System.Collections.Generic; using Autogenerated = Dapr.Client.Autogen.Grpc.v1; using FluentAssertions; using Xunit; using Microsoft.Extensions.Configuration; using Dapr.Client; using Grpc.Net.Client; using System.Net; using System.Threading.Tasks; namespace Dapr.Extensions.Configuration.Test { // These tests use the outdated TestHttpClient infrastructure because they need to // support testing with synchronous HTTP requests. // // Don't copy this pattern elsewhere. public class DaprSecretStoreConfigurationProviderTest { [Fact] public void AddDaprSecretStore_UsingDescriptors_WithoutStore_ReportsError() { var daprClient = new DaprClientBuilder() .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) .Build(); var ex = Assert.Throws(() => { var config = CreateBuilder() .AddDaprSecretStore(null, new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) .Build(); }); Assert.Contains("store", ex.Message); } [Fact] public void AddDaprSecretStore_UsingDescriptors_WithEmptyStore_ReportsError() { var daprClient = new DaprClientBuilder() .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) .Build(); var ex = Assert.Throws(() => { var config = CreateBuilder() .AddDaprSecretStore(string.Empty, new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) .Build(); }); Assert.Contains("The value cannot be null or empty", ex.Message); } [Fact] public void AddDaprSecretStore_UsingDescriptors_WithoutSecretDescriptors_ReportsError() { var daprClient = new DaprClientBuilder() .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) .Build(); var ex = Assert.Throws(() => { var config = CreateBuilder() .AddDaprSecretStore("store", (DaprSecretDescriptor[])null, daprClient) .Build(); }); Assert.Contains("secretDescriptors", ex.Message); } [Fact] public void AddDaprSecretStore_UsingDescriptors_WithoutClient_ReportsError() { var ex = Assert.Throws(() => { var config = CreateBuilder() .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, null) .Build(); }); Assert.Contains("client", ex.Message); } [Fact] public void AddDaprSecretStore_UsingDescriptors_WithZeroSecretDescriptors_ReportsError() { var daprClient = new DaprClientBuilder() .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) .Build(); var ex = Assert.Throws(() => { var config = CreateBuilder() .AddDaprSecretStore("store", new DaprSecretDescriptor[] { }, daprClient) .Build(); }); Assert.Contains("No secret descriptor was provided", ex.Message); } [Fact] public void AddDaprSecretStore_UsingDescriptors_DuplicateSecret_ReportsError() { var httpClient = new TestHttpClient() { Handler = async (entry) => { var secrets = new Dictionary() { { "secretName", "secret" }, { "SecretName", "secret" }}; await SendResponseWithSecrets(secrets, entry); } }; var daprClient = new DaprClientBuilder() .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) .Build(); var ex = Assert.Throws(() => { var config = CreateBuilder() .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) .Build(); }); Assert.Contains("Please remove any duplicates from your secret store.", ex.Message); } [Fact] public void LoadSecrets_FromSecretStoreThatReturnsOneValue() { // Configure Client var httpClient = new TestHttpClient() { Handler = async (entry) => { var secrets = new Dictionary() { { "secretName", "secret" } }; await SendResponseWithSecrets(secrets, entry); } }; var daprClient = new DaprClientBuilder() .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) .Build(); var config = CreateBuilder() .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) .Build(); config["secretName"].Should().Be("secret"); } [Fact] public void LoadSecrets_FromSecretStoreThatCanReturnsMultipleValues() { // Configure Client var httpClient = new TestHttpClient() { Handler = async (entry) => { var secrets = new Dictionary() { { "first_secret", "secret1" }, { "second_secret", "secret2" }}; await SendResponseWithSecrets(secrets, entry); } }; var daprClient = new DaprClientBuilder() .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) .Build(); var config = CreateBuilder() .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) .Build(); config["first_secret"].Should().Be("secret1"); config["second_secret"].Should().Be("secret2"); } //Here [Fact] public void AddDaprSecretStore_WithoutStore_ReportsError() { var daprClient = new DaprClientBuilder() .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) .Build(); var ex = Assert.Throws(() => { var config = CreateBuilder() .AddDaprSecretStore(null, daprClient) .Build(); }); Assert.Contains("store", ex.Message); } [Fact] public void AddDaprSecretStore_WithEmptyStore_ReportsError() { var daprClient = new DaprClientBuilder() .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = new TestHttpClient() }) .Build(); var ex = Assert.Throws(() => { var config = CreateBuilder() .AddDaprSecretStore(string.Empty, daprClient) .Build(); }); Assert.Contains("The value cannot be null or empty", ex.Message); } [Fact] public void AddDaprSecretStore_WithoutClient_ReportsError() { var ex = Assert.Throws(() => { var config = CreateBuilder() .AddDaprSecretStore("store", null) .Build(); }); Assert.Contains("client", ex.Message); } [Fact] public void AddDaprSecretStore_DuplicateSecret_ReportsError() { var httpClient = new TestHttpClient() { Handler = async (entry) => { var secrets = new Dictionary() { { "secretName", "secret" }, { "SecretName", "secret" }}; await SendBulkResponseWithSecrets(secrets, entry); } }; var daprClient = new DaprClientBuilder() .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) .Build(); var ex = Assert.Throws(() => { var config = CreateBuilder() .AddDaprSecretStore("store", daprClient) .Build(); }); Assert.Contains("Please remove any duplicates from your secret store.", ex.Message); } [Fact] public void BulkLoadSecrets_FromSecretStoreThatReturnsOneValue() { // Configure Client var httpClient = new TestHttpClient() { Handler = async (entry) => { var secrets = new Dictionary() { { "secretName", "secret" } }; await SendBulkResponseWithSecrets(secrets, entry); } }; var daprClient = new DaprClientBuilder() .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) .Build(); var config = CreateBuilder() .AddDaprSecretStore("store", daprClient) .Build(); config["secretName"].Should().Be("secret"); } [Fact] public void BulkLoadSecrets_FromSecretStoreThatCanReturnsMultipleValues() { // Configure Client var httpClient = new TestHttpClient() { Handler = async (entry) => { var secrets = new Dictionary() { { "first_secret", "secret1" }, { "second_secret", "secret2" }}; await SendBulkResponseWithSecrets(secrets, entry); } }; var daprClient = new DaprClientBuilder() .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient }) .Build(); var config = CreateBuilder() .AddDaprSecretStore("store", daprClient) .Build(); config["first_secret"].Should().Be("secret1"); config["second_secret"].Should().Be("secret2"); } private IConfigurationBuilder CreateBuilder() { return new ConfigurationBuilder(); } private async Task SendResponseWithSecrets(Dictionary secrets, TestHttpClient.Entry entry) { var secretResponse = new Autogenerated.GetSecretResponse(); secretResponse.Data.Add(secrets); var streamContent = await GrpcUtils.CreateResponseContent(secretResponse); var response = GrpcUtils.CreateResponse(HttpStatusCode.OK, streamContent); entry.Completion.SetResult(response); } private async Task SendBulkResponseWithSecrets(Dictionary secrets, TestHttpClient.Entry entry) { var getBulkSecretResponse = new Autogenerated.GetBulkSecretResponse(); foreach (var secret in secrets) { var secretsResponse = new Autogenerated.SecretResponse(); secretsResponse.Secrets[secret.Key] = secret.Value; getBulkSecretResponse.Data.Add(secret.Key, secretsResponse); } var streamContent = await GrpcUtils.CreateResponseContent(getBulkSecretResponse); var response = GrpcUtils.CreateResponse(HttpStatusCode.OK, streamContent); entry.Completion.SetResult(response); } } }