diff --git a/StevanFreeborn.SecureConfig.slnx b/StevanFreeborn.SecureConfig.slnx index 27522a1..8513f10 100644 --- a/StevanFreeborn.SecureConfig.slnx +++ b/StevanFreeborn.SecureConfig.slnx @@ -1,9 +1,11 @@ + + diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/.editorconfig b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/.editorconfig new file mode 100644 index 0000000..acaa91a --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/.editorconfig @@ -0,0 +1,6 @@ +[*.cs] + +dotnet_diagnostic.CA1303.severity = none; +dotnet_diagnostic.CA2007.severity = none; +dotnet_diagnostic.CA1822.severity = none; +dotnet_diagnostic.CA1812.severity = none; \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/DeleteCommandFactory.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/DeleteCommandFactory.cs new file mode 100644 index 0000000..3113fd6 --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/DeleteCommandFactory.cs @@ -0,0 +1,51 @@ +using System.CommandLine; + +using StevanFreeborn.Extensions.Configuration.Secure.Configuration; + +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands; + +internal sealed class DeleteCommandFactory( + ISecureConfig secureConfig, + ITerminal terminal +) : ICommandFactory +{ + private const string CommandName = "delete"; + private const string CommandDescription = "Delete the value for the given key"; + + private readonly Argument _keyArg = new("key") + { + Description = "The key whose value you want to remove." + }; + + private readonly ISecureConfig _secureConfig = secureConfig; + private readonly ITerminal _terminal = terminal; + + public Command Create() + { + var command = new Command(CommandName, CommandDescription) + { + _keyArg, + }; + + command.SetAction(HandleAsync); + + return command; + } + + private async Task HandleAsync(ParseResult result, CancellationToken ct) + { + var key = result.GetRequiredValue(_keyArg); + var isDeleted = await _secureConfig.DeleteAsync(key, ct); + + if (isDeleted) + { + _terminal.WriteLine($"Successfully removed value for {key}"); + return ExitCodes.Success; + } + else + { + _terminal.WriteLine($"Unable to remove value for {key}"); + return ExitCodes.KeyNotRemoved; + } + } +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/ExitCodes.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/ExitCodes.cs new file mode 100644 index 0000000..a064dbc --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/ExitCodes.cs @@ -0,0 +1,8 @@ +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands; + +internal static class ExitCodes +{ + public const int Success = 0; + public const int KeyNotFound = 1; + public const int KeyNotRemoved = 2; +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/GetCommandFactory.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/GetCommandFactory.cs new file mode 100644 index 0000000..6a62cd8 --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/GetCommandFactory.cs @@ -0,0 +1,80 @@ +using System.CommandLine; +using System.Text.Json; + +using StevanFreeborn.Extensions.Configuration.Secure.Cli.Json; +using StevanFreeborn.Extensions.Configuration.Secure.Configuration; + +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands; + +internal sealed class GetCommandFactory( + ISecureConfig secureConfig, + ITerminal terminal +) : ICommandFactory +{ + private const string CommandName = "get"; + private const string CommandDescription = "Retrieves the value for the given key"; + + private readonly Argument _keyArg = new("key") + { + Description = "The key whose value you want to retrieve." + }; + + private readonly Option _prettyOption = new("--pretty", "-p") + { + Description = "Indicates whether the value - if JSON - should be pretty printed or not" + }; + + private readonly ISecureConfig _secureConfig = secureConfig; + private readonly ITerminal _terminal = terminal; + + public Command Create() + { + var command = new Command(CommandName, CommandDescription) + { + _keyArg, + _prettyOption + }; + + command.SetAction(HandleAsync); + + return command; + } + + private async Task HandleAsync(ParseResult result, CancellationToken ct) + { + var key = result.GetRequiredValue(_keyArg); + var shouldPrintPretty = result.GetValue(_prettyOption); + var value = await _secureConfig.GetAsync(key, ct); + + if (value is null) + { + _terminal.WriteLine($"Unable to retrieve value for {key}"); + return ExitCodes.KeyNotFound; + } + + try + { + using var json = JsonDocument.Parse(value); + + if (shouldPrintPretty) + { + var options = new JsonSerializerOptions() + { + WriteIndented = true, + }; + var context = new CliJsonContext(options); + value = JsonSerializer.Serialize(json.RootElement, context.JsonElement); + } + else + { + value = JsonSerializer.Serialize(json.RootElement, CliJsonContext.Default.JsonElement); + } + } + catch (JsonException) + { + } + + _terminal.WriteLine(value); + return ExitCodes.Success; + } +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/ICommandFactory.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/ICommandFactory.cs new file mode 100644 index 0000000..90437ca --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/ICommandFactory.cs @@ -0,0 +1,8 @@ +using System.CommandLine; + +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands; + +internal interface ICommandFactory +{ + Command Create(); +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/ITerminal.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/ITerminal.cs new file mode 100644 index 0000000..206350d --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/ITerminal.cs @@ -0,0 +1,6 @@ +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands; + +internal interface ITerminal +{ + public void WriteLine(string value); +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/SetCommandFactory.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/SetCommandFactory.cs new file mode 100644 index 0000000..3c7e753 --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/SetCommandFactory.cs @@ -0,0 +1,63 @@ + +using System.CommandLine; +using System.Text.Json; + +using StevanFreeborn.Extensions.Configuration.Secure.Cli.Json; +using StevanFreeborn.Extensions.Configuration.Secure.Configuration; + +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands; + +internal sealed class SetCommandFactory( + ISecureConfig secureConfig, + ITerminal terminal +) : ICommandFactory +{ + private const string CommandName = "set"; + private const string CommandDescription = "Sets the value for the given key"; + + private readonly Argument _keyArg = new("key") + { + Description = "The key whose value you want to set." + }; + + private readonly Argument _valueArg = new("value") + { + Description = "The value that you want to set for the key." + }; + + private readonly ISecureConfig _secureConfig = secureConfig; + private readonly ITerminal _terminal = terminal; + + public Command Create() + { + var command = new Command(CommandName, CommandDescription) + { + _keyArg, + _valueArg, + }; + + command.SetAction(HandleAsync); + + return command; + } + + private async Task HandleAsync(ParseResult result, CancellationToken ct) + { + var key = result.GetRequiredValue(_keyArg); + var value = result.GetRequiredValue(_valueArg); + + try + { + using var json = JsonDocument.Parse(value); + value = JsonSerializer.Serialize(json.RootElement, CliJsonContext.Default.JsonElement); + } + catch (JsonException) + { + } + + await _secureConfig.SetAsync(key, value, ct); + + _terminal.WriteLine($"Successfully set value of {key}"); + return ExitCodes.Success; + } +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/Terminal.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/Terminal.cs new file mode 100644 index 0000000..a585421 --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Commands/Terminal.cs @@ -0,0 +1,9 @@ +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands; + +internal sealed class Terminal : ITerminal +{ + public void WriteLine(string value) + { + Console.WriteLine(value); + } +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/Base64KeyOptions.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/Base64KeyOptions.cs new file mode 100644 index 0000000..80b79de --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/Base64KeyOptions.cs @@ -0,0 +1,6 @@ +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Configuration; + +internal sealed class Base64KeyOptions +{ + public string Key { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/CryptoProviderOptions.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/CryptoProviderOptions.cs new file mode 100644 index 0000000..4ffa137 --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/CryptoProviderOptions.cs @@ -0,0 +1,6 @@ +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Configuration; + +internal sealed class CryptoProviderOptions +{ + public string Type { get; set; } = CryptoProviderTypes.Aes; +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/CryptoProviderTypes.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/CryptoProviderTypes.cs new file mode 100644 index 0000000..d0d0769 --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/CryptoProviderTypes.cs @@ -0,0 +1,6 @@ +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Configuration; + +internal static class CryptoProviderTypes +{ + public const string Aes = "aes"; +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/JsonStorageOptions.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/JsonStorageOptions.cs new file mode 100644 index 0000000..2542323 --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/JsonStorageOptions.cs @@ -0,0 +1,7 @@ +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Configuration; + +internal sealed class JsonStorageOptions +{ + public string DirectoryPath { get; set; } = AppContext.BaseDirectory; + public string FileName { get; set; } = "secure_config.json"; +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/KeyProviderOptions.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/KeyProviderOptions.cs new file mode 100644 index 0000000..e8b518c --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/KeyProviderOptions.cs @@ -0,0 +1,7 @@ +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Configuration; + +internal sealed class KeyProviderOptions +{ + public string Type { get; set; } = KeyProviderTypes.MachineId; + public Base64KeyOptions Base64 { get; set; } = new(); +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/KeyProviderTypes.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/KeyProviderTypes.cs new file mode 100644 index 0000000..b938c2d --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/KeyProviderTypes.cs @@ -0,0 +1,7 @@ +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Configuration; + +internal static class KeyProviderTypes +{ + public const string MachineId = "machineid"; + public const string Base64 = "base64"; +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/SecureConfigBuilderExtensions.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/SecureConfigBuilderExtensions.cs new file mode 100644 index 0000000..bcaaa9b --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/SecureConfigBuilderExtensions.cs @@ -0,0 +1,67 @@ +using StevanFreeborn.Extensions.Configuration.Secure.Configuration; + +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Configuration; + +internal static class SecureConfigBuilderExtensions +{ + public static ISecureConfigBuilder ApplyProfile( + this ISecureConfigBuilder builder, + SecureConfigProfile profile + ) + { + ConfigureStorage(builder, profile.Storage); + ConfigureKey(builder, profile.Key); + ConfigureCrypto(builder, profile.Crypto); + + return builder; + } + + private static void ConfigureStorage(ISecureConfigBuilder builder, StorageProviderOptions options) + { +#pragma warning disable CA1308 // Normalize strings to uppercase + switch (options.Type.ToLowerInvariant()) +#pragma warning restore CA1308 // Normalize strings to uppercase + { + case StorageProviderTypes.Json: + builder.UseJsonFileStorage(opt => + { + opt.DirectoryPath = options.Json.DirectoryPath; + opt.FileName = options.Json.FileName; + }); + break; + default: + throw new NotSupportedException($"Storage provider type '{options.Type}' is not supported."); + } + } + + private static void ConfigureKey(ISecureConfigBuilder builder, KeyProviderOptions options) + { +#pragma warning disable CA1308 // Normalize strings to uppercase + switch (options.Type.ToLowerInvariant()) +#pragma warning restore CA1308 // Normalize strings to uppercase + { + case KeyProviderTypes.MachineId: + builder.WithMachineIdKey(); + break; + case KeyProviderTypes.Base64: + builder.WithBase64EncryptionKey(options.Base64.Key); + break; + default: + throw new NotSupportedException($"Key provider type '{options.Type}' is not supported."); + } + } + + private static void ConfigureCrypto(ISecureConfigBuilder builder, CryptoProviderOptions options) + { +#pragma warning disable CA1308 // Normalize strings to uppercase + switch (options.Type.ToLowerInvariant()) +#pragma warning restore CA1308 // Normalize strings to uppercase + { + case CryptoProviderTypes.Aes: + builder.WithAesCryptoProvider(); + break; + default: + throw new NotSupportedException($"Crypto provider type '{options.Type}' is not supported."); + } + } +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/SecureConfigCliOptions.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/SecureConfigCliOptions.cs new file mode 100644 index 0000000..fe18e6d --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/SecureConfigCliOptions.cs @@ -0,0 +1,7 @@ +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Configuration; + +internal sealed class SecureConfigCliOptions +{ + public string DefaultProfile { get; set; } = "Default"; + public Dictionary Profiles { get; set; } = new(StringComparer.OrdinalIgnoreCase); +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/SecureConfigProfile.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/SecureConfigProfile.cs new file mode 100644 index 0000000..b504f38 --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/SecureConfigProfile.cs @@ -0,0 +1,8 @@ +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Configuration; + +internal sealed class SecureConfigProfile +{ + public StorageProviderOptions Storage { get; set; } = new(); + public KeyProviderOptions Key { get; set; } = new(); + public CryptoProviderOptions Crypto { get; set; } = new(); +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/StorageProviderOptions.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/StorageProviderOptions.cs new file mode 100644 index 0000000..5cf236f --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/StorageProviderOptions.cs @@ -0,0 +1,7 @@ +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Configuration; + +internal sealed class StorageProviderOptions +{ + public string Type { get; set; } = StorageProviderTypes.Json; + public JsonStorageOptions Json { get; set; } = new(); +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/StorageProviderTypes.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/StorageProviderTypes.cs new file mode 100644 index 0000000..f70ff14 --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Configuration/StorageProviderTypes.cs @@ -0,0 +1,6 @@ +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Configuration; + +internal static class StorageProviderTypes +{ + public const string Json = "json"; +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Json/CliJsonContext.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Json/CliJsonContext.cs new file mode 100644 index 0000000..4980035 --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Json/CliJsonContext.cs @@ -0,0 +1,10 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Json; + +[JsonSerializable(typeof(string))] +[JsonSerializable(typeof(JsonElement))] +internal sealed partial class CliJsonContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Program.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Program.cs new file mode 100644 index 0000000..062be99 --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/Program.cs @@ -0,0 +1,44 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +using StevanFreeborn.Extensions.Configuration.Secure; +using StevanFreeborn.Extensions.Configuration.Secure.Cli; +using StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands; +using StevanFreeborn.Extensions.Configuration.Secure.Cli.Configuration; +using StevanFreeborn.Extensions.Configuration.Secure.Cli.Json; + +await Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration(static config => + { + config.SetBasePath(AppContext.BaseDirectory) + .AddJsonFile("appsettings.json", false); + }) + .ConfigureLogging(static logging => logging.ClearProviders()) + .ConfigureServices((ctx, services) => + { + services.AddSingleton(args); + + services.AddSecureConfig(b => + { + var cliOptions = new SecureConfigCliOptions(); + ctx.Configuration.Bind(cliOptions); + + var profileName = ctx.Configuration.GetValue("profile") ?? cliOptions.DefaultProfile; + + if (cliOptions.Profiles.TryGetValue(profileName, out var profile) is false) + { + throw new InvalidOperationException($"Secure configuration profile '{profileName}' not found."); + } + + b.AddJsonAotContext(CliJsonContext.Default).ApplyProfile(profile); + }); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddHostedService(); + }) + .RunConsoleAsync(); \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/SecureConfigCli.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/SecureConfigCli.cs new file mode 100644 index 0000000..0e0ce71 --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/SecureConfigCli.cs @@ -0,0 +1,48 @@ +using System.CommandLine; + +using Microsoft.Extensions.Hosting; + +using StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands; + +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli; + +internal sealed class SecureConfigCli : IHostedService +{ + private readonly string[] _args; + private readonly IHostApplicationLifetime _lifetime; + private readonly RootCommand _rootCommand = new("secure-config is a command line utility used to manage your encrypted configuration") + { + new Option("--profile") + { + Description = "Allows specifying which profile to use from the appsettings.json file.", + }, + }; + + public SecureConfigCli( + string[] args, + IHostApplicationLifetime lifetime, + IEnumerable commandFactories + ) + { + _args = args; + _lifetime = lifetime; + + foreach (var factory in commandFactories) + { + _rootCommand.Add(factory.Create()); + } + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + var exitCode = await _rootCommand.Parse(_args[1..]).InvokeAsync(cancellationToken: cancellationToken); + Environment.ExitCode = exitCode; + + _lifetime.StopApplication(); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/StevanFreeborn.Extensions.Configuration.Secure.Cli.csproj b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/StevanFreeborn.Extensions.Configuration.Secure.Cli.csproj new file mode 100644 index 0000000..b7dbef1 --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Cli/StevanFreeborn.Extensions.Configuration.Secure.Cli.csproj @@ -0,0 +1,38 @@ + + + + secure-config + Exe + net10.0 + enable + enable + true + true + + latest + All + true + true + true + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure.Sample/Program.cs b/src/StevanFreeborn.Extensions.Configuration.Secure.Sample/Program.cs index 9afd9e8..37f305f 100644 --- a/src/StevanFreeborn.Extensions.Configuration.Secure.Sample/Program.cs +++ b/src/StevanFreeborn.Extensions.Configuration.Secure.Sample/Program.cs @@ -1,12 +1,10 @@ using System.Security.Cryptography; using System.Text; -using System.Text.Json; using System.Text.Json.Serialization.Metadata; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using StevanFreeborn.Extensions.Configuration.Secure; diff --git a/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests.csproj b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests.csproj new file mode 100644 index 0000000..20dbc32 --- /dev/null +++ b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests.csproj @@ -0,0 +1,52 @@ + + + + net10.0 + enable + enable + false + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + true + ./TestResults/Coverage/ + cobertura + [*]StevanFreeborn.Extensions.Configuration.Secure.Cli* + **/Program.cs + + + + + + + + + + + + + + + + diff --git a/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/DeleteCommandFactoryTests.cs b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/DeleteCommandFactoryTests.cs new file mode 100644 index 0000000..44b12c4 --- /dev/null +++ b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/DeleteCommandFactoryTests.cs @@ -0,0 +1,36 @@ +using System.CommandLine; + +using Moq; + +using StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands; +using StevanFreeborn.Extensions.Configuration.Secure.Configuration; + +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests.Unit.Commands; + +public class DeleteCommandFactoryTests +{ + private readonly Mock _mockSecureConfig = new(); + private readonly Mock _mockTerminal = new(); + private readonly DeleteCommandFactory _sut; + + public DeleteCommandFactoryTests() + { + _sut = new(_mockSecureConfig.Object, _mockTerminal.Object); + } + + [Fact] + public void Create_WhenCalled_ItShouldReturnExpectedCommand() + { + var expectedCommand = new Command("delete", "Delete the value for the given key") + { + new Argument("key") + { + Description = "The key whose value you want to remove." + }, + }; + + var result = _sut.Create(); + + result.Should().BeEquivalentTo(expectedCommand, static o => o.IgnoringCyclicReferences()); + } +} \ No newline at end of file diff --git a/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/DeleteCommandTests.cs b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/DeleteCommandTests.cs new file mode 100644 index 0000000..2a3f95c --- /dev/null +++ b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/DeleteCommandTests.cs @@ -0,0 +1,52 @@ +using System.CommandLine; + +using Moq; + +using StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands; +using StevanFreeborn.Extensions.Configuration.Secure.Configuration; + +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests.Unit.Commands; + +public class DeleteCommandTests +{ + private readonly Mock _mockSecureConfig = new(); + private readonly Mock _mockTerminal = new(); + private readonly Command _sut; + + public DeleteCommandTests() + { + _sut = new DeleteCommandFactory(_mockSecureConfig.Object, _mockTerminal.Object).Create(); + } + + [Fact] + public async Task Delete_WhenCalledWithoutKey_ItShouldReturnNonZeroExitCode() + { + var result = await _sut.Parse([]).InvokeAsync(); + + result.Should().NotBe(0); + } + + [Fact] + public async Task Delete_WhenCalledWithNonExistentKey_ItShouldReturnNonZeroExitCode() + { + var expectedKey = "key"; + + _mockSecureConfig.Setup(m => m.DeleteAsync(expectedKey, It.IsAny())).ReturnsAsync(false); + + var result = await _sut.Parse([expectedKey]).InvokeAsync(); + + result.Should().NotBe(0); + } + + [Fact] + public async Task Delete_WhenCalledWithExistingKey_ItShouldReturnZeroExitCode() + { + var expectedKey = "key"; + + _mockSecureConfig.Setup(m => m.DeleteAsync(expectedKey, It.IsAny())).ReturnsAsync(true); + + var result = await _sut.Parse([expectedKey]).InvokeAsync(); + + result.Should().Be(0); + } +} \ No newline at end of file diff --git a/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/GetCommandFactoryTests.cs b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/GetCommandFactoryTests.cs new file mode 100644 index 0000000..ce1cd64 --- /dev/null +++ b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/GetCommandFactoryTests.cs @@ -0,0 +1,41 @@ +using System.CommandLine; + +using Moq; + +using StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands; +using StevanFreeborn.Extensions.Configuration.Secure.Configuration; + +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests.Unit.Commands; + +public class GetCommandFactoryTests +{ + private readonly Mock _mockSecureConfig = new(); + private readonly Mock _mockTerminal = new(); + private readonly GetCommandFactory _sut; + + public GetCommandFactoryTests() + { + _sut = new(_mockSecureConfig.Object, _mockTerminal.Object); + } + + [Fact] + public void Create_WhenCalled_ItShouldReturnExpectedCommand() + { + var expectedCommand = new Command("get", "Retrieves the value for the given key") + { + new Argument("key") + { + Description = "The key whose value you want to retrieve." + }, + new Option("--pretty", "-p") + { + Description = "Indicates whether the value - if JSON - should be pretty printed or not" + }, + }; + + var result = _sut.Create(); + + result.Should().BeEquivalentTo(expectedCommand, static o => o.IgnoringCyclicReferences()); + result.Action.Should().NotBeNull(); + } +} \ No newline at end of file diff --git a/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/GetCommandTests.cs b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/GetCommandTests.cs new file mode 100644 index 0000000..dedb3b8 --- /dev/null +++ b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/GetCommandTests.cs @@ -0,0 +1,96 @@ +using System.CommandLine; +using System.Text.Json; + +using Moq; + +using StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands; +using StevanFreeborn.Extensions.Configuration.Secure.Configuration; + +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests.Unit.Commands; + +public class GetCommandTests +{ + private static readonly JsonSerializerOptions JsonOptions = new() + { + WriteIndented = true, + }; + private readonly Mock _mockSecureConfig = new(); + private readonly Mock _mockTerminal = new(); + private readonly Command _sut; + + public GetCommandTests() + { + _sut = new GetCommandFactory(_mockSecureConfig.Object, _mockTerminal.Object).Create(); + } + + [Fact] + public async Task Get_WhenCalledWithoutKeyArgument_ItShouldReturnNonZeroExitCode() + { + var result = await _sut.Parse([]).InvokeAsync(); + + result.Should().NotBe(0); + } + + [Fact] + public async Task Get_WhenCalledWithNonExistentKey_ItShouldReturnNonZeroExitCode() + { + var expectedKey = "key"; + + _mockSecureConfig + .Setup(m => m.GetAsync(expectedKey, It.IsAny())) + .ReturnsAsync((string?)null); + + var result = await _sut.Parse([expectedKey]).InvokeAsync(); + + result.Should().NotBe(0); + } + + [Fact] + public async Task Get_WhenCalledWithKeyForNonJsonValue_ItShouldReturnZeroExitCode() + { + var expectedKey = "key"; + var expectedValue = "value"; + + _mockSecureConfig + .Setup(m => m.GetAsync(expectedKey, It.IsAny())) + .ReturnsAsync(expectedValue); + + var result = await _sut.Parse([expectedKey]).InvokeAsync(); + + result.Should().Be(0); + } + + [Fact] + public async Task Get_WhenCalledWithKeyForJsonValue_ItShouldReturnZeroExitCode() + { + var expectedKey = "key"; + var expectedValue = JsonSerializer.Serialize(new { ApiKey = "Key" }); + + _mockSecureConfig + .Setup(m => m.GetAsync(expectedKey, It.IsAny())) + .ReturnsAsync(expectedValue); + + var result = await _sut.Parse([expectedKey]).InvokeAsync(); + + result.Should().Be(0); + } + + [Fact] + public async Task Get_WhenCalledWithKeyForJsonValueAndPrettyPrint_ItShouldReturnZeroExitCode() + { + var expectedKey = "key"; + var testValue = new { ApiKey = "Key" }; + var expectedValue = JsonSerializer.Serialize(testValue); + var expectedPrintedValue = JsonSerializer.Serialize(testValue, JsonOptions); + + _mockSecureConfig + .Setup(m => m.GetAsync(expectedKey, It.IsAny())) + .ReturnsAsync(expectedValue); + + var result = await _sut.Parse([expectedKey, "-p"]).InvokeAsync(); + + result.Should().Be(0); + + _mockTerminal.Verify(m => m.WriteLine(expectedPrintedValue), Times.Once()); + } +} \ No newline at end of file diff --git a/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/SetCommandFactoryTests.cs b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/SetCommandFactoryTests.cs new file mode 100644 index 0000000..c0e2588 --- /dev/null +++ b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/SetCommandFactoryTests.cs @@ -0,0 +1,41 @@ +using System.CommandLine; + +using Moq; + +using StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands; +using StevanFreeborn.Extensions.Configuration.Secure.Configuration; + +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests.Unit.Commands; + +public class SetCommandFactoryTests +{ + private readonly Mock _mockSecureConfig = new(); + private readonly Mock _mockTerminal = new(); + private readonly SetCommandFactory _sut; + + public SetCommandFactoryTests() + { + _sut = new(_mockSecureConfig.Object, _mockTerminal.Object); + } + + [Fact] + public void Create_WhenCalled_ItShouldReturnExpectedCommand() + { + var expectedCommand = new Command("set", "Sets the value for the given key") + { + new Argument("key") + { + Description = "The key whose value you want to set.", + }, + new Argument("value") + { + Description = "The value that you want to set for the key." + }, + }; + + var result = _sut.Create(); + + result.Should().BeEquivalentTo(expectedCommand, static o => o.IgnoringCyclicReferences()); + result.Action.Should().NotBeNull(); + } +} \ No newline at end of file diff --git a/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/SetCommandTests.cs b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/SetCommandTests.cs new file mode 100644 index 0000000..42ec805 --- /dev/null +++ b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/SetCommandTests.cs @@ -0,0 +1,63 @@ +using System.CommandLine; +using System.Text.Json; + +using Moq; + +using StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands; +using StevanFreeborn.Extensions.Configuration.Secure.Configuration; + +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests.Unit.Commands; + +public class SetCommandTests +{ + private readonly Mock _mockSecureConfig = new(); + private readonly Mock _mockTerminal = new(); + private readonly Command _sut; + + public SetCommandTests() + { + _sut = new SetCommandFactory(_mockSecureConfig.Object, _mockTerminal.Object).Create(); + } + + [Fact] + public async Task Set_WhenCalledWithoutKey_ItShouldReturnNonZeroExitCode() + { + var result = await _sut.Parse([]).InvokeAsync(); + + result.Should().NotBe(0); + } + + [Fact] + public async Task Set_WhenCalledWithoutValue_ItShouldReturnNonZeroExitCode() + { + var result = await _sut.Parse(["key"]).InvokeAsync(); + + result.Should().NotBe(0); + } + + [Fact] + public async Task Set_WhenCalledWithKeyAndValue_ItShouldReturnZeroExitCode() + { + var expectedKey = "key"; + var expectedValue = "value"; + + var result = await _sut.Parse([expectedKey, expectedValue]).InvokeAsync(); + + result.Should().Be(0); + + _mockSecureConfig.Verify(m => m.SetAsync(expectedKey, expectedValue, It.IsAny()), Times.Once()); + } + + [Fact] + public async Task Set_WhenCalledWithKeyAndJsonValue_ItShouldReturnZeroExitCode() + { + var expectedKey = "key"; + var expectedValue = JsonSerializer.Serialize(new { ApiKey = "ApiKey" }); + + var result = await _sut.Parse([expectedKey, expectedValue]).InvokeAsync(); + + result.Should().Be(0); + + _mockSecureConfig.Verify(m => m.SetAsync(expectedKey, expectedValue, It.IsAny()), Times.Once()); + } +} \ No newline at end of file diff --git a/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/TerminalTests.cs b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/TerminalTests.cs new file mode 100644 index 0000000..3f5b389 --- /dev/null +++ b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Commands/TerminalTests.cs @@ -0,0 +1,36 @@ +using StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands; + +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests.Unit.Commands; + +public class TerminalTests : IDisposable +{ + private readonly StringWriter _stringWriter = new(); + private readonly Terminal _sut = new(); + + public TerminalTests() + { + Console.SetOut(_stringWriter); + } + + [Fact] + public void WriteLine_WhenCalled_ItShouldWriteToConsole() + { + var message = "test message"; + + _sut.WriteLine(message); + + _stringWriter.ToString().Should().Be($"test message{Environment.NewLine}"); + } + + public void Dispose() + { + var standardOutput = new StreamWriter(Console.OpenStandardOutput()) + { + AutoFlush = true + }; + + Console.SetOut(standardOutput); + + GC.SuppressFinalize(this); + } +} \ No newline at end of file diff --git a/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Configuration/SecureConfigBuilderExtensionsTests.cs b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Configuration/SecureConfigBuilderExtensionsTests.cs new file mode 100644 index 0000000..50f086d --- /dev/null +++ b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/Configuration/SecureConfigBuilderExtensionsTests.cs @@ -0,0 +1,125 @@ +using Moq; + + +using StevanFreeborn.Extensions.Configuration.Secure.Cli.Configuration; +using StevanFreeborn.Extensions.Configuration.Secure.Configuration; + +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests.Unit.Configuration; + +public class SecureConfigBuilderExtensionsTests +{ + private readonly Mock _mockBuilder = new(); + + [Fact] + public void ApplyProfile_WhenCalledWithValidOptions_ItShouldConfigureBuilder() + { + _mockBuilder.Setup(static m => m.UseJsonFileStorage(It.IsAny>())) + .Callback>(static a => a(new Storage.JsonStorageOptions())) + .Returns(_mockBuilder.Object); + + _mockBuilder.Setup(static m => m.WithMachineIdKey()).Returns(_mockBuilder.Object); + _mockBuilder.Setup(static m => m.WithBase64EncryptionKey(It.IsAny())).Returns(_mockBuilder.Object); + _mockBuilder.Setup(static m => m.WithAesCryptoProvider()).Returns(_mockBuilder.Object); + + var profile = new SecureConfigProfile + { + Storage = new StorageProviderOptions + { + Type = "json", + Json = new JsonStorageOptions { DirectoryPath = "dir", FileName = "file.json" } + }, + Key = new KeyProviderOptions + { + Type = "machineid" + }, + Crypto = new CryptoProviderOptions + { + Type = "aes" + } + }; + + var result = _mockBuilder.Object.ApplyProfile(profile); + + result.Should().Be(_mockBuilder.Object); + _mockBuilder.Verify(static m => m.UseJsonFileStorage(It.IsAny>()), Times.Once()); + _mockBuilder.Verify(static m => m.WithMachineIdKey(), Times.Once()); + _mockBuilder.Verify(static m => m.WithAesCryptoProvider(), Times.Once()); + } + + [Fact] + public void ApplyProfile_WhenCalledWithBase64Key_ItShouldConfigureBuilder() + { + var builderMock = new Mock(); + + var profile = new SecureConfigProfile + { + Storage = new StorageProviderOptions { Type = "json", Json = new Cli.Configuration.JsonStorageOptions() }, + Key = new KeyProviderOptions { Type = "base64", Base64 = new Base64KeyOptions { Key = "test-key" } }, + Crypto = new CryptoProviderOptions { Type = "aes" } + }; + + builderMock.Object.ApplyProfile(profile); + + builderMock.Verify(m => m.WithBase64EncryptionKey("test-key"), Times.Once()); + } + + [Fact] + public void ApplyProfile_WhenUnsupportedStorage_ItShouldThrowNotSupportedException() + { + var builderMock = new Mock(); + var profile = new SecureConfigProfile + { + Storage = new StorageProviderOptions { Type = "unsupported" }, + Key = new KeyProviderOptions { Type = "machineid" }, + Crypto = new CryptoProviderOptions { Type = "aes" } + }; + + Action act = () => builderMock.Object.ApplyProfile(profile); + + act.Should().Throw().WithMessage("Storage provider type 'unsupported' is not supported."); + } + + [Fact] + public void ApplyProfile_WhenUnsupportedKey_ItShouldThrowNotSupportedException() + { + var builderMock = new Mock(); + var profile = new SecureConfigProfile + { + Storage = new StorageProviderOptions { Type = "json", Json = new Cli.Configuration.JsonStorageOptions() }, + Key = new KeyProviderOptions { Type = "unsupported" }, + Crypto = new CryptoProviderOptions { Type = "aes" } + }; + + Action act = () => builderMock.Object.ApplyProfile(profile); + + act.Should().Throw().WithMessage("Key provider type 'unsupported' is not supported."); + } + + [Fact] + public void ApplyProfile_WhenUnsupportedCrypto_ItShouldThrowNotSupportedException() + { + var builderMock = new Mock(); + var profile = new SecureConfigProfile + { + Storage = new StorageProviderOptions { Type = "json", Json = new Cli.Configuration.JsonStorageOptions() }, + Key = new KeyProviderOptions { Type = "machineid" }, + Crypto = new CryptoProviderOptions { Type = "unsupported" } + }; + + Action act = () => builderMock.Object.ApplyProfile(profile); + + act.Should().Throw().WithMessage("Crypto provider type 'unsupported' is not supported."); + } + + [Fact] + public void SecureConfigCliOptions_ShouldBeCovered() + { + var options = new SecureConfigCliOptions + { + DefaultProfile = "default", + Profiles = [] + }; + options.DefaultProfile.Should().Be("default"); + options.Profiles.Should().NotBeNull(); + } +} \ No newline at end of file diff --git a/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/SecureConfigCliTests.cs b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/SecureConfigCliTests.cs new file mode 100644 index 0000000..355b863 --- /dev/null +++ b/tests/StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests/Unit/SecureConfigCliTests.cs @@ -0,0 +1,51 @@ +using System.CommandLine; + + +using Microsoft.Extensions.Hosting; + + +using Moq; + + +using StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands; + +namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Tests.Unit; + +public class SecureConfigCliTests +{ + private readonly Mock _mockLifetime = new(); + private readonly Mock _mockCommandFactory = new(); + + public SecureConfigCliTests() + { + _mockCommandFactory.Setup(m => m.Create()).Returns(new Command("test-command")); + } + + [Fact] + public void Constructor_WhenCalled_ItShouldCallCreateOnAllFactoriesPassedIn() + { + var sut = new SecureConfigCli(["secure-config"], _mockLifetime.Object, [_mockCommandFactory.Object]); + _mockCommandFactory.Verify(static m => m.Create(), Times.Once()); + } + + [Fact] + public async Task StartAsync_WhenCalled_ItShouldRunTheCLI() + { + // Arrange + var sut = new SecureConfigCli(["secure-config", "--help"], _mockLifetime.Object, [_mockCommandFactory.Object]); + + // Act + await sut.StartAsync(CancellationToken.None); + + // Assert + _mockLifetime.Verify(m => m.StopApplication(), Times.Once()); + } + + [Fact] + public async Task StopAsync_WhenCalled_ItShouldNotThrowException() + { + var sut = new SecureConfigCli(["secure-config"], _mockLifetime.Object, [_mockCommandFactory.Object]); + var act = async () => await sut.StopAsync(CancellationToken.None); + await act.Should().NotThrowAsync(); + } +} \ No newline at end of file diff --git a/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Integration/SecureConfigExtensionsTests.cs b/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Integration/SecureConfigExtensionsTests.cs index 66aad68..ad82f88 100644 --- a/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Integration/SecureConfigExtensionsTests.cs +++ b/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Integration/SecureConfigExtensionsTests.cs @@ -1,4 +1,3 @@ -using System.Security.Cryptography; using System.Text.Json.Serialization; using Microsoft.Extensions.Configuration; diff --git a/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Integration/SecureConfigOptionsIntegrationTests.cs b/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Integration/SecureConfigOptionsIntegrationTests.cs index 1688bcd..1817068 100644 --- a/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Integration/SecureConfigOptionsIntegrationTests.cs +++ b/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Integration/SecureConfigOptionsIntegrationTests.cs @@ -1,4 +1,3 @@ -using System.Security.Cryptography; using System.Text.Json.Serialization; using Microsoft.Extensions.Configuration; diff --git a/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/StevanFreeborn.Extensions.Configuration.Secure.Tests.csproj b/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/StevanFreeborn.Extensions.Configuration.Secure.Tests.csproj index a330012..e80ee77 100644 --- a/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/StevanFreeborn.Extensions.Configuration.Secure.Tests.csproj +++ b/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/StevanFreeborn.Extensions.Configuration.Secure.Tests.csproj @@ -13,8 +13,12 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + @@ -23,6 +27,18 @@ + + true + ./TestResults/Coverage/ + cobertura + [StevanFreeborn.Extensions.Configuration.Secure]* + **/Program.cs + + + + + + diff --git a/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Unit/Configuration/SecureConfigBuilderTests.cs b/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Unit/Configuration/SecureConfigBuilderTests.cs index f2219df..43078af 100644 --- a/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Unit/Configuration/SecureConfigBuilderTests.cs +++ b/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Unit/Configuration/SecureConfigBuilderTests.cs @@ -84,8 +84,7 @@ public class SecureConfigBuilderTests { var act = () => _sut.UseJsonFileStorage((Action)null!); - act.Should().Throw() - .WithParameterName("configure"); + act.Should().Throw().WithParameterName("configure"); } [Fact] @@ -104,8 +103,7 @@ public class SecureConfigBuilderTests { var act = () => _sut.AddJsonAotContext(null!); - act.Should().Throw() - .WithParameterName("context"); + act.Should().Throw().WithParameterName("context"); } [Fact] diff --git a/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Unit/Configuration/SecureConfigSourceTests.cs b/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Unit/Configuration/SecureConfigSourceTests.cs index 7a29345..7a6fb78 100644 --- a/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Unit/Configuration/SecureConfigSourceTests.cs +++ b/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Unit/Configuration/SecureConfigSourceTests.cs @@ -24,8 +24,7 @@ public class SecureConfigSourceTests loggerFactory: _mockLoggerFactory.Object ); - act.Should().Throw() - .WithParameterName("storageProvider"); + act.Should().Throw().WithParameterName("storageProvider"); } [Fact] @@ -37,8 +36,7 @@ public class SecureConfigSourceTests loggerFactory: _mockLoggerFactory.Object ); - act.Should().Throw() - .WithParameterName("cryptoProvider"); + act.Should().Throw().WithParameterName("cryptoProvider"); } [Fact] @@ -50,8 +48,7 @@ public class SecureConfigSourceTests loggerFactory: null! ); - act.Should().Throw() - .WithParameterName("loggerFactory"); + act.Should().Throw().WithParameterName("loggerFactory"); } [Fact] @@ -87,6 +84,7 @@ public class SecureConfigSourceTests public void Build_WhenCalled_ItShouldCreateLoggerFromFactory() { var mockLogger = new Mock>(); + _mockLoggerFactory .Setup(f => f.CreateLogger(typeof(SecureConfigProvider).FullName!)) .Returns(mockLogger.Object); diff --git a/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Unit/Configuration/SecureConfigTests.cs b/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Unit/Configuration/SecureConfigTests.cs index b684c6a..0bea547 100644 --- a/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Unit/Configuration/SecureConfigTests.cs +++ b/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Unit/Configuration/SecureConfigTests.cs @@ -1,4 +1,3 @@ -using System.Runtime.CompilerServices; using System.Text.Json; using System.Text.Json.Serialization;