diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml new file mode 100644 index 00000000..11a1e86c --- /dev/null +++ b/.github/workflows/code-coverage.yml @@ -0,0 +1,48 @@ +name: "Code Coverage" + +on: + push: + branches: [ main ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ main ] + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + language: [ 'csharp' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Coverlet coverage test + uses: b3b00/coverlet-action@1.2.4 + with: + testProject: 'src/Arcus.Security.Tests.Unit/Arcus.Security.Tests.Unit.csproj' + excludes: '[Arcus.Security.Tests.*]*,[Arcus.Security.Providers.*]*' + threshold: 80 + output: 'lcov.info' + outputFormat: 'lcov' + + - name: ReportGenerator + uses: danielpalme/ReportGenerator-GitHub-Action@5.1.13 + if: always() + with: + reports: 'src/Arcus.Security.Tests.Unit/lcov.info' + targetdir: 'coveragereport' + + - name: Upload coverage report artifact + uses: actions/upload-artifact@v2.2.3 + if: always() + with: + name: CoverageReport + path: coveragereport \ No newline at end of file diff --git a/src/Arcus.Security.Core/Extensions/ISecretProviderExtensions.Deprecated.cs b/src/Arcus.Security.Core/Extensions/ISecretProviderExtensions.Deprecated.cs index 6ba70ec2..50c36631 100644 --- a/src/Arcus.Security.Core/Extensions/ISecretProviderExtensions.Deprecated.cs +++ b/src/Arcus.Security.Core/Extensions/ISecretProviderExtensions.Deprecated.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; using GuardNet; @@ -10,6 +11,7 @@ namespace Arcus.Security.Core.Extensions /// Extensions on the to retrieve several secret values based on configured allowed versions. /// // ReSharper disable once InconsistentNaming + [ExcludeFromCodeCoverage] public static class ISecretProviderExtensions { /// diff --git a/src/Arcus.Security.Providers.AzureKeyVault/KeyVaultSecretProvider.cs b/src/Arcus.Security.Providers.AzureKeyVault/KeyVaultSecretProvider.cs index 3eab9c8d..71c37601 100644 --- a/src/Arcus.Security.Providers.AzureKeyVault/KeyVaultSecretProvider.cs +++ b/src/Arcus.Security.Providers.AzureKeyVault/KeyVaultSecretProvider.cs @@ -454,7 +454,7 @@ private TResponse InteractWithKeyVault( } throw new InvalidOperationException( - "Old Azure Key Vault client does not support asynchronous operations, please use the new Azure Key Vault secret provider overloads that uses the new Azure SDK"); + "Old Azure Key Vault client does not support synchronous operations, please use the new Azure Key Vault secret provider overloads that uses the new Azure SDK"); }); isSuccessful = true; diff --git a/src/Arcus.Security.Tests.Unit/Arcus.Security.Tests.Unit.csproj b/src/Arcus.Security.Tests.Unit/Arcus.Security.Tests.Unit.csproj index 383bdc4b..1de08105 100644 --- a/src/Arcus.Security.Tests.Unit/Arcus.Security.Tests.Unit.csproj +++ b/src/Arcus.Security.Tests.Unit/Arcus.Security.Tests.Unit.csproj @@ -1,13 +1,21 @@ - + - net6.0;netcoreapp3.1 + net6.0 false CS0618 + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/Arcus.Security.Tests.Unit/AzureFunctions/IFunctionsHostBuilderTests.cs b/src/Arcus.Security.Tests.Unit/AzureFunctions/IFunctionsHostBuilderTests.cs index 17c91757..c9608b9e 100644 --- a/src/Arcus.Security.Tests.Unit/AzureFunctions/IFunctionsHostBuilderTests.cs +++ b/src/Arcus.Security.Tests.Unit/AzureFunctions/IFunctionsHostBuilderTests.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using Arcus.Security.Core; using Arcus.Security.Core.Caching; @@ -8,11 +6,11 @@ using Arcus.Security.Tests.Unit.Core.Stubs; using Microsoft.Azure.Functions.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; using Xunit; namespace Arcus.Security.Tests.Unit.AzureFunctions { + // ReSharper disable once InconsistentNaming public class IFunctionsHostBuilderTests { [Fact] @@ -30,6 +28,21 @@ public async Task ConfigureSecretStore_WithoutSecretProviders_ThrowsException() await Assert.ThrowsAsync(() => secretProvider.GetSecretAsync("ignored-key")); } + [Fact] + public async Task ConfigureSecretStoreWithConfig_WithoutSecretProviders_ThrowsException() + { + // Arrange + var builder = new StubFunctionsHostBuilder(); + + // Act + builder.ConfigureSecretStore((context, config, stores) => { }); + + // Assert + IServiceProvider serviceProvider = builder.Build(); + var secretProvider = serviceProvider.GetRequiredService(); + await Assert.ThrowsAsync(() => secretProvider.GetSecretAsync("ignored-key")); + } + [Fact] public async Task ConfigureSecretStore_WithoutFoundSecretProvider_ThrowsException() { @@ -46,6 +59,22 @@ public async Task ConfigureSecretStore_WithoutFoundSecretProvider_ThrowsExceptio await Assert.ThrowsAsync(() => secretProvider.GetSecretAsync("ignored-key")); } + [Fact] + public async Task ConfigureSecretStoreWithConfig_WithoutFoundSecretProvider_ThrowsException() + { + // Arrange + var builder = new StubFunctionsHostBuilder(); + var emptyProvider = new InMemorySecretProvider(); + + // Act + builder.ConfigureSecretStore((context, config, stores) => stores.AddProvider(emptyProvider)); + + // Assert + IServiceProvider serviceProvider = builder.Build(); + var secretProvider = serviceProvider.GetRequiredService(); + await Assert.ThrowsAsync(() => secretProvider.GetSecretAsync("ignored-key")); + } + [Fact] public async Task ConfigureSecretStore_WithoutFoundCachedProvider_ThrowsException() { diff --git a/src/Arcus.Security.Tests.Unit/CommandLine/SecretStoreBuilderExtensionsTests.cs b/src/Arcus.Security.Tests.Unit/CommandLine/SecretStoreBuilderExtensionsTests.cs index 482e7215..4e0fb938 100644 --- a/src/Arcus.Security.Tests.Unit/CommandLine/SecretStoreBuilderExtensionsTests.cs +++ b/src/Arcus.Security.Tests.Unit/CommandLine/SecretStoreBuilderExtensionsTests.cs @@ -73,6 +73,52 @@ public void AddCommandLine_WithMutateSecretWithoutArguments_Fails() // Assert Assert.ThrowsAny(() => builder.Build()); } + + [Fact] + public void AddCommandLine_WithMutateSecretWithArguments_Fails() + { + // Arrange + string secretName = "MySecret", expected = "P@ssw0rd"; + var arguments = new[] {$"--{secretName}={expected}"}; + var builder = new HostBuilder(); + + // Act + builder.ConfigureSecretStore((config, stores) => + { + stores.AddCommandLine(arguments, mutateSecretName: name => name); + }); + + // Assert + using (IHost host = builder.Build()) + { + var secretProvider = host.Services.GetRequiredService(); + Assert.Equal(expected, secretProvider.GetSecret(secretName).Value); + Assert.Equal(expected, secretProvider.GetRawSecret(secretName)); + } + } + + [Fact] + public void AddCommandLine_WithMutateSecretWithArgumentsWithUnknownSecretName_Fails() + { + // Arrange + string secretName = "MySecret", expected = "P@ssw0rd"; + var arguments = new[] {$"--{secretName}={expected}"}; + var builder = new HostBuilder(); + + // Act + builder.ConfigureSecretStore((config, stores) => + { + stores.AddCommandLine(arguments, mutateSecretName: name => name); + }); + + // Assert + using (IHost host = builder.Build()) + { + var secretProvider = host.Services.GetRequiredService(); + Assert.Throws(() => secretProvider.GetSecret("NotExisting")); + Assert.Throws(() => secretProvider.GetRawSecret("NotExisting")); + } + } [Fact] public void AddCommandLine_WithNameWithMutateSecretWithoutArguments_Fails() diff --git a/src/Arcus.Security.Tests.Unit/Core/CriticalExceptionsTests.cs b/src/Arcus.Security.Tests.Unit/Core/CriticalExceptionsTests.cs new file mode 100644 index 00000000..ca4a3138 --- /dev/null +++ b/src/Arcus.Security.Tests.Unit/Core/CriticalExceptionsTests.cs @@ -0,0 +1,57 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Arcus.Security.Core; +using Arcus.Security.Tests.Unit.Core.Stubs; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Arcus.Security.Tests.Unit.Core +{ + public class CriticalExceptionsTests + { + [Fact] + public async Task GetSecret_WithThrownCriticalException_FailsWithCriticalException() + { + // Arrange + var services = new ServiceCollection(); + + // Act + services.AddSecretStore(stores => + { + stores.AddProvider(new SaboteurSecretProvider(new IOException("Something happened!"))) + .AddCriticalException(); + }); + + // Assert + IServiceProvider serviceProvider = services.BuildServiceProvider(); + var secretProvider = serviceProvider.GetRequiredService(); + Assert.Throws(() => secretProvider.GetSecret("Some.Secret")); + Assert.Throws(() => secretProvider.GetRawSecret("Some.Secret")); + await Assert.ThrowsAsync(() => secretProvider.GetSecretAsync("Some.Secret")); + await Assert.ThrowsAsync(() => secretProvider.GetRawSecretAsync("Some.Secret")); + } + + [Fact] + public async Task GetSecret_WithThrownNonCriticalException_FailsWithSecretNotFoundException() + { + // Arrange + var services = new ServiceCollection(); + + // Act + services.AddSecretStore(stores => + { + stores.AddProvider(new SaboteurSecretProvider(new AccessViolationException("Something happened!"))) + .AddCriticalException(); + }); + + // Assert + IServiceProvider serviceProvider = services.BuildServiceProvider(); + var secretProvider = serviceProvider.GetRequiredService(); + Assert.Throws(() => secretProvider.GetSecret("Some.Secret")); + Assert.Throws(() => secretProvider.GetRawSecret("Some.Secret")); + await Assert.ThrowsAsync(() => secretProvider.GetSecretAsync("Some.Secret")); + await Assert.ThrowsAsync(() => secretProvider.GetRawSecretAsync("Some.Secret")); + } + } +} diff --git a/src/Arcus.Security.Tests.Unit/Core/Extensions/IHostBuilderExtensionsTests.cs b/src/Arcus.Security.Tests.Unit/Core/Extensions/IHostBuilderExtensionsTests.cs index 23d66dcc..d6c33730 100644 --- a/src/Arcus.Security.Tests.Unit/Core/Extensions/IHostBuilderExtensionsTests.cs +++ b/src/Arcus.Security.Tests.Unit/Core/Extensions/IHostBuilderExtensionsTests.cs @@ -877,7 +877,7 @@ public void ConfigureSecretStore_WithDuplicateNames_FailsWhenRetrievingTypedCach using (IHost host = builder.Build()) { var store = host.Services.GetRequiredService(); - Assert.Throws(() => store.GetProvider(name)); + Assert.Throws(() => store.GetCachedProvider(name)); } } diff --git a/src/Arcus.Security.Tests.Unit/Core/MutatedSecretNameCachedSecretProviderTests.cs b/src/Arcus.Security.Tests.Unit/Core/MutatedSecretNameCachedSecretProviderTests.cs index de031941..517d8423 100644 --- a/src/Arcus.Security.Tests.Unit/Core/MutatedSecretNameCachedSecretProviderTests.cs +++ b/src/Arcus.Security.Tests.Unit/Core/MutatedSecretNameCachedSecretProviderTests.cs @@ -79,6 +79,26 @@ public async Task InvalidateSecret_WithMutation_Succeeds() await provider.InvalidateSecretAsync("Arcus:KeyVault:Secret"); } + [Fact] + public void ConfigureSecretStore_WithDefault_FailsToRetrieveCacheConfiguration() + { + // Arrange + var builder = new HostBuilder(); + + // Act + builder.ConfigureSecretStore((config, stores) => + { + stores.AddProvider(new InMemorySecretProvider()); + }); + + // Assert + using (IHost host = builder.Build()) + { + var provider = host.Services.GetRequiredService(); + Assert.Throws(() => provider.Configuration); + } + } + [Fact] public void CreateCachedProvider_WithoutImplementation_Throws() { diff --git a/src/Arcus.Security.Tests.Unit/Core/NamedSecretProviderTests.cs b/src/Arcus.Security.Tests.Unit/Core/NamedSecretProviderTests.cs new file mode 100644 index 00000000..93585f30 --- /dev/null +++ b/src/Arcus.Security.Tests.Unit/Core/NamedSecretProviderTests.cs @@ -0,0 +1,34 @@ +using System; +using Arcus.Security.Core; +using Arcus.Security.Providers.AzureKeyVault; +using Arcus.Security.Tests.Unit.Core.Stubs; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Arcus.Security.Tests.Unit.Core +{ + public class NamedSecretProviderTests + { + [Fact] + public void GetCachedProvider_WithWrongCastType_Fails() + { + // Arrange + var services = new ServiceCollection(); + var name = "TestProvider"; + + // Act + services.AddSecretStore(stores => + { + stores.AddProvider( + new InMemoryCachedSecretProvider(("Secret.Name", "Secret.Value")), + options => options.Name = name); + }); + + // Assert + IServiceProvider serviceProvider = services.BuildServiceProvider(); + var secretProvider = serviceProvider.GetRequiredService(); + Assert.ThrowsAny( + () => secretProvider.GetCachedProvider(name)); + } + } +} diff --git a/src/Arcus.Security.Tests.Unit/Core/SecretProviderCachingExtensionsTests.cs b/src/Arcus.Security.Tests.Unit/Core/SecretProviderCachingExtensionsTests.cs index e8737ddf..bb84f270 100644 --- a/src/Arcus.Security.Tests.Unit/Core/SecretProviderCachingExtensionsTests.cs +++ b/src/Arcus.Security.Tests.Unit/Core/SecretProviderCachingExtensionsTests.cs @@ -2,6 +2,8 @@ using Arcus.Security.Tests.Unit.Core.Stubs; using System; using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; using Xunit; namespace Arcus.Security.Tests.Unit.Core @@ -9,18 +11,64 @@ namespace Arcus.Security.Tests.Unit.Core public class SecretProviderCachingExtensionsTests { [Fact] - public async Task SecretProviderCachingExtensions_TwoCallsWithinCacheInterval_ShouldGetSameValueTwice() + public async Task WithCachingTimeSpanMemoryCache_TwoCallsWithinCacheInterval_ShouldGetSameValueTwice() { // Arrange - string secretKeyValue = Guid.NewGuid().ToString("N"); + var secretKeyValue = Guid.NewGuid().ToString("N"); var testSecretProvider = new TestSecretProviderStub(secretKeyValue); - string keyName = "MyValue"; + var keyName = "MyValue"; + var cache = new MemoryCache(Options.Create(new MemoryCacheOptions())); + + // Act + ICachedSecretProvider cachedSecretProvider = testSecretProvider.WithCaching(TimeSpan.FromSeconds(3), cache); + string firstValue = await cachedSecretProvider.GetRawSecretAsync(keyName); + + ChangeInternalCachedSecret(testSecretProvider); + string secondValue = await cachedSecretProvider.GetRawSecretAsync(keyName); + + // Assert + Assert.Equal(firstValue, secondValue); + Assert.Equal(1, testSecretProvider.CallsMadeSinceCreation); + } + + [Fact] + public async Task WithCachingTimeSpanMemoryCache_TwoCallsOutsideCacheInterval_ShouldGetDifferentValue() + { + // Arrange + var secretKeyValue = Guid.NewGuid().ToString("N"); + var testSecretProvider = new TestSecretProviderStub(secretKeyValue); + var keyName = "MyValue"; + var cache = new MemoryCache(Options.Create(new MemoryCacheOptions())); + + // Act + ICachedSecretProvider cachedSecretProvider = testSecretProvider.WithCaching(TimeSpan.FromMilliseconds(100), cache); + string firstValue = await cachedSecretProvider.GetRawSecretAsync(keyName); + + await Task.Delay(TimeSpan.FromMilliseconds(150)); + string newSecretValue = Guid.NewGuid().ToString("N"); + ChangeInternalCachedSecret(testSecretProvider, newSecretValue); + string secondValue = await cachedSecretProvider.GetRawSecretAsync(keyName); + + // Assert + Assert.Equal(secretKeyValue, firstValue); + Assert.Equal(newSecretValue, secondValue); + Assert.Equal(2, testSecretProvider.CallsMadeSinceCreation); + } + + [Fact] + public async Task WithCachingTimeSpan_TwoCallsWithinCacheInterval_ShouldGetSameValueTwice() + { + // Arrange + var secretKeyValue = Guid.NewGuid().ToString("N"); + var testSecretProvider = new TestSecretProviderStub(secretKeyValue); + var keyName = "MyValue"; // Act ICachedSecretProvider cachedSecretProvider = testSecretProvider.WithCaching(TimeSpan.FromSeconds(3)); - var firstValue = await cachedSecretProvider.GetRawSecretAsync(keyName); - testSecretProvider.SecretValue = Guid.NewGuid().ToString("N"); // Change actual value on the internal secret provider ! - var secondValue = await cachedSecretProvider.GetRawSecretAsync(keyName); + string firstValue = await cachedSecretProvider.GetRawSecretAsync(keyName); + + ChangeInternalCachedSecret(testSecretProvider); + string secondValue = await cachedSecretProvider.GetRawSecretAsync(keyName); // Assert Assert.Equal(firstValue, secondValue); @@ -28,26 +76,56 @@ public async Task SecretProviderCachingExtensions_TwoCallsWithinCacheInterval_Sh } [Fact] - public async Task SecretProviderCachingExtensions_TwoCallsOutsideCacheInterval_ShouldGetDifferentValue() + public async Task WithCachingTimeSpan_TwoCallsOutsideCacheInterval_ShouldGetDifferentValue() { // Arrange - string secretKeyValue = Guid.NewGuid().ToString("N"); + var secretKeyValue = Guid.NewGuid().ToString("N"); var testSecretProvider = new TestSecretProviderStub(secretKeyValue); - - string keyName = "MyValue"; + var keyName = "MyValue"; // Act ICachedSecretProvider cachedSecretProvider = testSecretProvider.WithCaching(TimeSpan.FromMilliseconds(100)); - var firstValue = await cachedSecretProvider.GetRawSecretAsync(keyName); + string firstValue = await cachedSecretProvider.GetRawSecretAsync(keyName); + await Task.Delay(TimeSpan.FromMilliseconds(150)); string newSecretValue = Guid.NewGuid().ToString("N"); - testSecretProvider.SecretValue = newSecretValue; // Change actual value on the internal secret provider ! - var secondValue = await cachedSecretProvider.GetRawSecretAsync(keyName); + ChangeInternalCachedSecret(testSecretProvider, newSecretValue); + string secondValue = await cachedSecretProvider.GetRawSecretAsync(keyName); // Assert Assert.Equal(secretKeyValue, firstValue); Assert.Equal(newSecretValue, secondValue); Assert.Equal(2, testSecretProvider.CallsMadeSinceCreation); } + + [Fact] + public async Task WithCachingDefault_TwoCallsWithinCacheInterval_ShouldGetSameValueTwice() + { + // Arrange + var secretKeyValue = Guid.NewGuid().ToString("N"); + var testSecretProvider = new TestSecretProviderStub(secretKeyValue); + var keyName = "MyValue"; + + // Act + ICachedSecretProvider cachedSecretProvider = testSecretProvider.WithCaching(); + string firstValue = await cachedSecretProvider.GetRawSecretAsync(keyName); + + ChangeInternalCachedSecret(testSecretProvider); + string secondValue = await cachedSecretProvider.GetRawSecretAsync(keyName); + + // Assert + Assert.Equal(firstValue, secondValue); + Assert.Equal(1, testSecretProvider.CallsMadeSinceCreation); + } + + private static void ChangeInternalCachedSecret(TestSecretProviderStub testSecretProvider) + { + ChangeInternalCachedSecret(testSecretProvider, Guid.NewGuid().ToString("N")); + } + + private static void ChangeInternalCachedSecret(TestSecretProviderStub testSecretProvider, string secretValue) + { + testSecretProvider.SecretValue = secretValue; + } } } diff --git a/src/Arcus.Security.Tests.Unit/Core/Stubs/SaboteurSecretProvider.cs b/src/Arcus.Security.Tests.Unit/Core/Stubs/SaboteurSecretProvider.cs index 24e75e27..329f6f62 100644 --- a/src/Arcus.Security.Tests.Unit/Core/Stubs/SaboteurSecretProvider.cs +++ b/src/Arcus.Security.Tests.Unit/Core/Stubs/SaboteurSecretProvider.cs @@ -8,7 +8,7 @@ namespace Arcus.Security.Tests.Unit.Core.Stubs /// /// Represents a that 'sabotage' the secret retrieval by throwing a user-defined exception. /// - public class SaboteurSecretProvider: ISecretProvider + public class SaboteurSecretProvider: ISyncSecretProvider { private readonly Exception _exception; @@ -48,5 +48,29 @@ public Task GetSecretAsync(string secretName) { throw _exception; } + + /// + /// Retrieves the secret value, based on the given name + /// + /// The name of the secret key + /// Returns the secret key. + /// Thrown when the is blank. + /// Thrown when the secret was not found, using the given name. + public string GetRawSecret(string secretName) + { + throw _exception; + } + + /// + /// Retrieves the secret value, based on the given name + /// + /// The name of the secret key + /// Returns a that contains the secret key + /// Thrown when the is blank. + /// Thrown when the secret was not found, using the given name. + public Secret GetSecret(string secretName) + { + throw _exception; + } } } diff --git a/src/Arcus.Security.Tests.Unit/Core/VersionedSecretTests.cs b/src/Arcus.Security.Tests.Unit/Core/VersionedSecretTests.cs index a0525a7b..56a8830c 100644 --- a/src/Arcus.Security.Tests.Unit/Core/VersionedSecretTests.cs +++ b/src/Arcus.Security.Tests.Unit/Core/VersionedSecretTests.cs @@ -23,7 +23,8 @@ public async Task GetRawSecret_WithVersion_ReturnsAll() var services = new ServiceCollection(); var secretName = "MySecret"; var amountOfVersions = 2; - var inMemory = new InMemorySecretVersionProvider(secretName, "secretValue", amountOfVersions); + var secretValue = "secretValue"; + var inMemory = new InMemorySecretVersionProvider(secretName, secretValue, amountOfVersions); services.AddSecretStore(stores => { stores.AddProvider(inMemory, options => options.AddVersionedSecret(secretName, amountOfVersions)); @@ -31,9 +32,17 @@ public async Task GetRawSecret_WithVersion_ReturnsAll() IServiceProvider serviceProvider = services.BuildServiceProvider(); var secretProvider = serviceProvider.GetRequiredService(); + var versionedProvider = (IVersionedSecretProvider) secretProvider; - IEnumerable secretValues = await secretProvider.GetRawSecretsAsync(secretName); - Assert.Equal(amountOfVersions, secretValues.Count()); + AssertCollectionCount(await secretProvider.GetSecretsAsync(secretName), amountOfVersions, secret => Assert.Equal(secretValue, secret.Value)); + AssertCollectionCount(await secretProvider.GetRawSecretsAsync(secretName), amountOfVersions, value => Assert.Equal(secretValue, value)); + AssertCollectionCount(await versionedProvider.GetSecretsAsync(secretName, amountOfVersions), amountOfVersions, secret => Assert.Equal(secretValue, secret.Value)); + AssertCollectionCount(await versionedProvider.GetRawSecretsAsync(secretName, amountOfVersions), amountOfVersions, value => Assert.Equal(secretValue, value)); + } + + private static void AssertCollectionCount(IEnumerable sequence, int assertionLength, Action assertion) + { + Assert.Collection(sequence, Enumerable.Repeat(assertion, assertionLength).ToArray()); } [Fact] diff --git a/src/Arcus.Security.Tests.Unit/KeyVault/KeyVaultSecretProviderTests.cs b/src/Arcus.Security.Tests.Unit/KeyVault/KeyVaultSecretProviderTests.cs index 47499593..4e1bc79c 100644 --- a/src/Arcus.Security.Tests.Unit/KeyVault/KeyVaultSecretProviderTests.cs +++ b/src/Arcus.Security.Tests.Unit/KeyVault/KeyVaultSecretProviderTests.cs @@ -94,7 +94,7 @@ public async Task KeyVaultSecretProvider_GetsRawSecretAsync_AfterRetriedTooManyR // Arrange string expected = $"secret-value-{Guid.NewGuid()}"; string secretName = $"secret-name-{Guid.NewGuid()}"; - KeyVaultSecretProvider provider = CreateSecretProviderWithTooManyRequestSimulation(expected); + KeyVaultSecretProvider provider = CreateOldSecretProviderWithTooManyRequestSimulation(expected); // Act string actual = await provider.GetRawSecretAsync(secretName); @@ -110,19 +110,19 @@ public async Task KeyVaultSecretProvider_GetsSecretAsync_AfterRetriedTooManyRequ string expected = $"secret-value-{Guid.NewGuid()}"; string secretName = $"secret-name-{Guid.NewGuid()}"; DateTime expirationDate = DateTime.UtcNow; - KeyVaultSecretProvider provider = CreateSecretProviderWithTooManyRequestSimulation(expected, expirationDate); // Act - Secret actual = await provider.GetSecretAsync(secretName); + KeyVaultSecretProvider provider = CreateOldSecretProviderWithTooManyRequestSimulation(expected, expirationDate); // Assert - Assert.NotNull(actual); - Assert.Equal(expected, actual.Value); - Assert.NotNull(actual.Version); - Assert.Equal(expirationDate, actual.Expires); + Secret actualFromAsync = await provider.GetSecretAsync(secretName); + Assert.NotNull(actualFromAsync); + Assert.Equal(expected, actualFromAsync.Value); + Assert.NotNull(actualFromAsync.Version); + Assert.Equal(expirationDate, actualFromAsync.Expires); } - private static KeyVaultSecretProvider CreateSecretProviderWithTooManyRequestSimulation(string expected, DateTime? expirationDate = null) + private static KeyVaultSecretProvider CreateOldSecretProviderWithTooManyRequestSimulation(string expected, DateTime? expirationDate = null) { // Arrange var keyVaultClient = new SimulatedKeyVaultClient(