Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: use coverlet code coverage #396

Merged
Merged
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
48 changes: 48 additions & 0 deletions .github/workflows/code-coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: "Code Coverage"
fgheysels marked this conversation as resolved.
Show resolved Hide resolved

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/[email protected]
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/[email protected]
if: always()
with:
reports: 'src/Arcus.Security.Tests.Unit/lcov.info'
targetdir: 'coveragereport'

- name: Upload coverage report artifact
uses: actions/[email protected]
if: always()
with:
name: CoverageReport
path: coveragereport
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using GuardNet;
Expand All @@ -10,6 +11,7 @@ namespace Arcus.Security.Core.Extensions
/// Extensions on the <see cref="ISecretProvider"/> to retrieve several secret values based on configured allowed versions.
/// </summary>
// ReSharper disable once InconsistentNaming
[ExcludeFromCodeCoverage]
public static class ISecretProviderExtensions
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ private TResponse InteractWithKeyVault<TResponse>(
}

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;
Expand Down
12 changes: 10 additions & 2 deletions src/Arcus.Security.Tests.Unit/Arcus.Security.Tests.Unit.csproj
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net6.0;netcoreapp3.1</TargetFrameworks>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
<NoWarn>CS0618</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Bogus" Version="34.0.2" />
<PackageReference Include="coverlet.collector" Version="3.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.msbuild" Version="3.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.8" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="Moq" Version="4.14.6" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Arcus.Security.Core;
using Arcus.Security.Core.Caching;
using Arcus.Security.Tests.Unit.AzureFunctions.Stubs;
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]
Expand All @@ -30,6 +28,21 @@ public async Task ConfigureSecretStore_WithoutSecretProviders_ThrowsException()
await Assert.ThrowsAsync<SecretNotFoundException>(() => 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<ISecretProvider>();
await Assert.ThrowsAsync<SecretNotFoundException>(() => secretProvider.GetSecretAsync("ignored-key"));
}

[Fact]
public async Task ConfigureSecretStore_WithoutFoundSecretProvider_ThrowsException()
{
Expand All @@ -46,6 +59,22 @@ public async Task ConfigureSecretStore_WithoutFoundSecretProvider_ThrowsExceptio
await Assert.ThrowsAsync<SecretNotFoundException>(() => 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<ISecretProvider>();
await Assert.ThrowsAsync<SecretNotFoundException>(() => secretProvider.GetSecretAsync("ignored-key"));
}

[Fact]
public async Task ConfigureSecretStore_WithoutFoundCachedProvider_ThrowsException()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,52 @@ public void AddCommandLine_WithMutateSecretWithoutArguments_Fails()
// Assert
Assert.ThrowsAny<ArgumentException>(() => 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<ISecretProvider>();
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<ISecretProvider>();
Assert.Throws<SecretNotFoundException>(() => secretProvider.GetSecret("NotExisting"));
Assert.Throws<SecretNotFoundException>(() => secretProvider.GetRawSecret("NotExisting"));
}
}

[Fact]
public void AddCommandLine_WithNameWithMutateSecretWithoutArguments_Fails()
Expand Down
57 changes: 57 additions & 0 deletions src/Arcus.Security.Tests.Unit/Core/CriticalExceptionsTests.cs
Original file line number Diff line number Diff line change
@@ -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<IOException>();
});

// Assert
IServiceProvider serviceProvider = services.BuildServiceProvider();
var secretProvider = serviceProvider.GetRequiredService<ISecretProvider>();
Assert.Throws<IOException>(() => secretProvider.GetSecret("Some.Secret"));
Assert.Throws<IOException>(() => secretProvider.GetRawSecret("Some.Secret"));
await Assert.ThrowsAsync<IOException>(() => secretProvider.GetSecretAsync("Some.Secret"));
await Assert.ThrowsAsync<IOException>(() => 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<IOException>();
});

// Assert
IServiceProvider serviceProvider = services.BuildServiceProvider();
var secretProvider = serviceProvider.GetRequiredService<ISecretProvider>();
Assert.Throws<SecretNotFoundException>(() => secretProvider.GetSecret("Some.Secret"));
Assert.Throws<SecretNotFoundException>(() => secretProvider.GetRawSecret("Some.Secret"));
await Assert.ThrowsAsync<SecretNotFoundException>(() => secretProvider.GetSecretAsync("Some.Secret"));
await Assert.ThrowsAsync<SecretNotFoundException>(() => secretProvider.GetRawSecretAsync("Some.Secret"));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -877,7 +877,7 @@ public void ConfigureSecretStore_WithDuplicateNames_FailsWhenRetrievingTypedCach
using (IHost host = builder.Build())
{
var store = host.Services.GetRequiredService<ISecretStore>();
Assert.Throws<InvalidOperationException>(() => store.GetProvider<InMemoryCachedSecretProvider>(name));
Assert.Throws<InvalidOperationException>(() => store.GetCachedProvider<InMemoryCachedSecretProvider>(name));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ICachedSecretProvider>();
Assert.Throws<NotSupportedException>(() => provider.Configuration);
}
}

[Fact]
public void CreateCachedProvider_WithoutImplementation_Throws()
{
Expand Down
34 changes: 34 additions & 0 deletions src/Arcus.Security.Tests.Unit/Core/NamedSecretProviderTests.cs
Original file line number Diff line number Diff line change
@@ -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<ISecretStore>();
Assert.ThrowsAny<InvalidCastException>(
() => secretProvider.GetCachedProvider<KeyVaultCachedSecretProvider>(name));
}
}
}
Loading