diff --git a/AspNetCore.Diagnostics.HealthChecks.sln b/AspNetCore.Diagnostics.HealthChecks.sln index 114b388d59..ce1fe825b7 100644 --- a/AspNetCore.Diagnostics.HealthChecks.sln +++ b/AspNetCore.Diagnostics.HealthChecks.sln @@ -73,6 +73,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HealthChecks.Elasticsearch" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HealthChecks.EventStore", "src\HealthChecks.EventStore\HealthChecks.EventStore.csproj", "{39667845-526D-46ED-90F0-05ED6B8814F1}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HealthChecks.AzureKeyVault", "src\HealthChecks.AzureKeyVault\HealthChecks.AzureKeyVault.csproj", "{A6414860-EBAE-43E4-8109-DE745DA15C43}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "test\UnitTests\UnitTests.csproj", "{300A1B42-EA00-480A-AC43-007EBC7CE472}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -187,6 +191,14 @@ Global {39667845-526D-46ED-90F0-05ED6B8814F1}.Debug|Any CPU.Build.0 = Debug|Any CPU {39667845-526D-46ED-90F0-05ED6B8814F1}.Release|Any CPU.ActiveCfg = Release|Any CPU {39667845-526D-46ED-90F0-05ED6B8814F1}.Release|Any CPU.Build.0 = Release|Any CPU + {A6414860-EBAE-43E4-8109-DE745DA15C43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6414860-EBAE-43E4-8109-DE745DA15C43}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6414860-EBAE-43E4-8109-DE745DA15C43}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6414860-EBAE-43E4-8109-DE745DA15C43}.Release|Any CPU.Build.0 = Release|Any CPU + {300A1B42-EA00-480A-AC43-007EBC7CE472}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {300A1B42-EA00-480A-AC43-007EBC7CE472}.Debug|Any CPU.Build.0 = Debug|Any CPU + {300A1B42-EA00-480A-AC43-007EBC7CE472}.Release|Any CPU.ActiveCfg = Release|Any CPU + {300A1B42-EA00-480A-AC43-007EBC7CE472}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -219,6 +231,8 @@ Global {6FB1E70A-2915-4810-BCA4-AF38010AF949} = {2A3FD988-2BB8-43CF-B3A2-B70E648259D4} {8ACAEE4F-55EA-452F-A5EF-9D99EA9885F9} = {2A3FD988-2BB8-43CF-B3A2-B70E648259D4} {39667845-526D-46ED-90F0-05ED6B8814F1} = {2A3FD988-2BB8-43CF-B3A2-B70E648259D4} + {A6414860-EBAE-43E4-8109-DE745DA15C43} = {2A3FD988-2BB8-43CF-B3A2-B70E648259D4} + {300A1B42-EA00-480A-AC43-007EBC7CE472} = {FF4414C2-8863-4ADA-8A1D-4B9F25C361FE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2B8C62A1-11B6-469F-874C-A02443256568} diff --git a/README.md b/README.md index b887991960..6acb297452 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ HealthChecks packages include health checks for: - System: Disk Storage, Private Memory, Virtual Memory - Azure Service Bus: EventHub, Queue and Topics - Azure Storage: Blob, Queue and Table +- Azure Key Vault - Azure DocumentDb - Amazon DynamoDb - Amazon S3 @@ -44,6 +45,7 @@ Install-Package AspNetCore.HealthChecks.Redis Install-Package AspNetCore.HealthChecks.EventStore Install-Package AspNetCore.HealthChecks.AzureStorage Install-Package AspNetCore.HealthChecks.AzureServiceBus +Install-Package AspNetCore.HealthChecks.AzureKeyVault Install-Package AspNetCore.HealthChecks.MySql Install-Package AspNetCore.HealthChecks.DocumentDb Install-Package AspNetCore.HealthChecks.SqLite diff --git a/build.ps1 b/build.ps1 index aa4ac3af0f..8bd405b6ea 100644 --- a/build.ps1 +++ b/build.ps1 @@ -38,13 +38,24 @@ echo "build: Build version suffix is $buildSuffix" exec { & dotnet build AspNetCore.Diagnostics.HealthChecks.sln -c Release --version-suffix=$buildSuffix -v q /nologo } +echo "Running unit tests" + +try { + +Push-Location -Path .\test\UnitTests + exec { & dotnet test} +} finally { + Pop-Location +} + + if (-Not (Test-Path 'env:APPVEYOR')) { exec { & docker-compose up -d } } echo "compose up done" -echo "running tests" +echo "Running functional tests" try { @@ -75,7 +86,8 @@ if ($suffix -eq "") { exec { & dotnet pack .\src\HealthChecks.Oracle\HealthChecks.Oracle.csproj -c Release -o ..\..\artifacts --include-symbols --no-build } exec { & dotnet pack .\src\HealthChecks.System\HealthChecks.System.csproj -c Release -o ..\..\artifacts --include-symbols --no-build } exec { & dotnet pack .\src\HealthChecks.Network\HealthChecks.Network.csproj -c Release -o ..\..\artifacts --include-symbols --no-build } - exec { & dotnet pack .\src\HealthChecks.Aws.S3\HealthChecks.Aws.S3.csproj -c Release -o ..\..\artifacts --include-symbols --no-build } + exec { & dotnet pack .\src\HealthChecks.Aws.S3\HealthChecks.Aws.S3.csproj -c Release -o ..\..\artifacts --include-symbols --no-build } + exec { & dotnet pack .\src\HealthChecks.HealthChecks.AzureKeyVault\HealthChecks.AzureKeyVault.csproj -c Release -o ..\..\artifacts --include-symbols --no-build } exec { & dotnet pack .\src\HealthChecks.UI\HealthChecks.UI.csproj -c Release -o ..\..\artifacts --include-symbols --no-build } exec { & dotnet pack .\src\HealthChecks.UI.Client\HealthChecks.UI.Client.csproj -c Release -o ..\..\artifacts --include-symbols --no-build } exec { & dotnet pack .\src\HealthChecks.Publisher.ApplicationInsights\HealthChecks.Publisher.ApplicationInsights.csproj -c Release -o ..\..\artifacts --include-symbols --no-build } @@ -102,7 +114,8 @@ else { exec { & dotnet pack .\src\HealthChecks.Oracle\HealthChecks.Oracle.csproj -c Release -o ..\..\artifacts --include-symbols --no-build --version-suffix=$suffix } exec { & dotnet pack .\src\HealthChecks.System\HealthChecks.System.csproj -c Release -o ..\..\artifacts --include-symbols --no-build --version-suffix=$suffix } exec { & dotnet pack .\src\HealthChecks.Network\HealthChecks.Network.csproj -c Release -o ..\..\artifacts --include-symbols --no-build --version-suffix=$suffix } - exec { & dotnet pack .\src\HealthChecks.Aws.S3\HealthChecks.Aws.S3.csproj -c Release -o ..\..\artifacts --include-symbols --no-build --version-suffix=$suffix } + exec { & dotnet pack .\src\HealthChecks.Aws.S3\HealthChecks.Aws.S3.csproj -c Release -o ..\..\artifacts --include-symbols --no-build --version-suffix=$suffix } + exec { & dotnet pack .\src\HealthChecks.AzureKeyVault\HealthChecks.AzureKeyVault.csproj -c Release -o ..\..\artifacts --include-symbols --no-build --version-suffix=$suffix } exec { & dotnet pack .\src\HealthChecks.UI\HealthChecks.UI.csproj -c Release -o ..\..\artifacts --include-symbols --no-build --version-suffix=$suffix } exec { & dotnet pack .\src\HealthChecks.UI.Client\HealthChecks.UI.Client.csproj -c Release -o ..\..\artifacts --include-symbols --no-build --version-suffix=$suffix } exec { & dotnet pack .\src\HealthChecks.Publisher.ApplicationInsights\HealthChecks.Publisher.ApplicationInsights.csproj -c Release -o ..\..\artifacts --include-symbols --no-build --version-suffix=$suffix } diff --git a/build/dependencies.props b/build/dependencies.props index e1ae744727..42d225c45b 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -48,9 +48,13 @@ 2.1.1 1.1.5 2.1.1 + 2.1.1 2.7.2 2.1.3 3.3.29 + 3.0.2 + 4.4.1 + 1.0.3 @@ -77,11 +81,12 @@ 2.2.0 2.2.0 2.2.0 - 2.2.0 + 2.2.1 2.2.4 2.2.2 2.2.0 2.2.0 2.2.0 + 2.2.0 \ No newline at end of file diff --git a/src/HealthChecks.AzureKeyVault/AzureKeyVaultHealthCheck.cs b/src/HealthChecks.AzureKeyVault/AzureKeyVaultHealthCheck.cs new file mode 100644 index 0000000000..27d41545e0 --- /dev/null +++ b/src/HealthChecks.AzureKeyVault/AzureKeyVaultHealthCheck.cs @@ -0,0 +1,75 @@ +using Microsoft.Azure.KeyVault; +using Microsoft.Azure.Services.AppAuthentication; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.IdentityModel.Clients.ActiveDirectory; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using static Microsoft.Azure.KeyVault.KeyVaultClient; + +namespace HealthChecks.AzureKeyVault +{ + public class AzureKeyVaultHealthCheck : IHealthCheck + { + private readonly AzureKeyVaultOptions _keyVaultOptions; + + public AzureKeyVaultHealthCheck(AzureKeyVaultOptions keyVaultOptions) + { + if (!Uri.TryCreate(keyVaultOptions.KeyVaultUrlBase, UriKind.Absolute, out var _)) + { + throw new ArgumentException("KeyVaultUrlBase must be a valid Uri"); + } + + _keyVaultOptions = keyVaultOptions; + } + public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + var currentSecret = string.Empty; + + try + { + var client = GetClient(_keyVaultOptions); + foreach (var secretIdentifier in _keyVaultOptions.Secrets) + { + currentSecret = secretIdentifier; + await client.GetSecretAsync(_keyVaultOptions.KeyVaultUrlBase, secretIdentifier, cancellationToken); + } + + return HealthCheckResult.Healthy(); + } + catch (Exception ex) + { + var secretException = new Exception($"{currentSecret} secret error - {ex.Message}", ex); + return new HealthCheckResult(context.Registration.FailureStatus, exception: secretException); + } + } + + private KeyVaultClient GetClient(AzureKeyVaultOptions options) + { + if (string.IsNullOrEmpty(options.ClientId)) + { + var azureServiceTokenProvider = new AzureServiceTokenProvider(); + return new KeyVaultClient(new AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback)); + } + else + { + return new KeyVaultClient(GetToken); + } + } + + public async Task GetToken(string authority, string resource, string scope) + { + var authContext = new AuthenticationContext(authority); + ClientCredential clientCred = new ClientCredential(_keyVaultOptions.ClientId, _keyVaultOptions.ClientSecret); + AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred); + + if (result == null) + throw new InvalidOperationException($"[{nameof(AzureKeyVaultHealthCheck)}] - Failed to obtain the JWT token"); + + return result.AccessToken; + } + } +} diff --git a/src/HealthChecks.AzureKeyVault/AzureKeyVaultMsiHealthCheck.cs b/src/HealthChecks.AzureKeyVault/AzureKeyVaultMsiHealthCheck.cs new file mode 100644 index 0000000000..049bd74acc --- /dev/null +++ b/src/HealthChecks.AzureKeyVault/AzureKeyVaultMsiHealthCheck.cs @@ -0,0 +1,51 @@ +using Microsoft.Azure.KeyVault; +using Microsoft.Azure.Services.AppAuthentication; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using static Microsoft.Azure.KeyVault.KeyVaultClient; + +namespace HealthChecks.AzureKeyVault +{ + public class AzureKeyVaultMsiHealthCheck : IHealthCheck + { + private readonly AzureKeyVaultOptions _keyVaultOptions; + + public AzureKeyVaultMsiHealthCheck(AzureKeyVaultOptions keyVaultOptions) + { + if (string.IsNullOrEmpty(keyVaultOptions.KeyVaultUrlBase)) + { + throw new ArgumentNullException(nameof(keyVaultOptions.KeyVaultUrlBase)); + } + + _keyVaultOptions = keyVaultOptions; + } + public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + var currentSecret = string.Empty; + + try + { + var azureServiceTokenProvider = new AzureServiceTokenProvider(); + + var client = new KeyVaultClient(new AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback)); + + foreach (var secretIdentifier in _keyVaultOptions.Secrets) + { + currentSecret = secretIdentifier; + await client.GetSecretAsync(_keyVaultOptions.KeyVaultUrlBase, secretIdentifier, cancellationToken); + } + + return HealthCheckResult.Healthy(); + } + catch (Exception ex) + { + var secretException = new Exception($"{currentSecret} secret error - {ex.Message}", ex); + return new HealthCheckResult(context.Registration.FailureStatus, exception: secretException); + } + } + } +} diff --git a/src/HealthChecks.AzureKeyVault/AzureKeyVaultOptions.cs b/src/HealthChecks.AzureKeyVault/AzureKeyVaultOptions.cs new file mode 100644 index 0000000000..abcc4eaff0 --- /dev/null +++ b/src/HealthChecks.AzureKeyVault/AzureKeyVaultOptions.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace HealthChecks.AzureKeyVault +{ + public class AzureKeyVaultOptions + { + internal List Secrets { get; } = new List(); + internal string KeyVaultUrlBase { get; set; } + internal string ClientId { get; set; } + internal string ClientSecret { get; set; } + + + /// + /// Configures remote Azure Key Vault Url service + /// + /// + /// + public AzureKeyVaultOptions UseKeyVaultUrl(string keyVaultUrlBase) + { + KeyVaultUrlBase = keyVaultUrlBase; + return this; + } + + /// + /// Azure key vault connection is performed using provided Client Id and Client Secret + /// + /// Azure Key Vault base url - https://[vaultname].vault.azure.net/ + /// Registered application Id + /// Registered application secret + /// + public AzureKeyVaultOptions UseClientSecrets(string clientId, string clientSecret) + { + if(string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(clientSecret)) + { + throw new ArgumentNullException("ClientId and ClientSecret parameters should not be empty"); + } + + ClientId = clientId; + ClientSecret = clientSecret; + + return this; + } + + /// + /// Add a Azure Key Vault secret to be checked + /// + /// + /// + public AzureKeyVaultOptions AddSecret(string secretIdentifier) + { + if(!Secrets.Contains(secretIdentifier)) + { + Secrets.Add(secretIdentifier); + } + + return this; + } + } +} diff --git a/src/HealthChecks.AzureKeyVault/DependencyInjection/AzureKeyVaultHealthChecksBuilderExtensions.cs b/src/HealthChecks.AzureKeyVault/DependencyInjection/AzureKeyVaultHealthChecksBuilderExtensions.cs new file mode 100644 index 0000000000..77754d61f0 --- /dev/null +++ b/src/HealthChecks.AzureKeyVault/DependencyInjection/AzureKeyVaultHealthChecksBuilderExtensions.cs @@ -0,0 +1,37 @@ +using HealthChecks.AzureKeyVault; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class AzureKeyVaultHealthChecksBuilderExtensions + { + /// + /// Add a health check for Azure Key Vault. Default behaviour is using Managed Service Identity, to use Client Secrets call UseClientSecrets in setup action + /// + /// The . + /// Setup action to configure Azure Key Vault options + /// The health check name. Optional. If null the type name 'azurekeyvault' will be used for the name. + /// + /// The that should be reported when the health check fails. Optional. If null then + /// the default status of will be reported. + /// + /// A list of tags that can be used to filter sets of health checks. Optional. + /// The . + public static IHealthChecksBuilder AddAzureKeyVault(this IHealthChecksBuilder builder, Action setup, + string name = default, HealthStatus? failureStatus = default, IEnumerable tags = default) + { + var options = new AzureKeyVaultOptions(); + setup?.Invoke(options); + + return builder.Add(new HealthCheckRegistration( + name ?? "azurekeyvault", + sp => new AzureKeyVaultHealthCheck(options), + failureStatus, + tags)); + } + } +} diff --git a/src/HealthChecks.AzureKeyVault/HealthChecks.AzureKeyVault.csproj b/src/HealthChecks.AzureKeyVault/HealthChecks.AzureKeyVault.csproj new file mode 100644 index 0000000000..225c4021b9 --- /dev/null +++ b/src/HealthChecks.AzureKeyVault/HealthChecks.AzureKeyVault.csproj @@ -0,0 +1,27 @@ + + + $(NetStandardTargetVersion) + $(PackageLicenseUrl) + $(PackageProjectUrl) + HealthCheck;Azure Key Vault;Secrets + HealthChecks.AzureKeyVault is the health check package for Azure Key Vault secrets + $(HealthCheckKeyVault) + $(RepositoryUrl) + $(Company) + $(Authors) + latest + AspNetCore.HealthChecks.AzureKeyVault + $(PublishRepositoryUrl) + $(AllowedOutputExtensionsInPackageBuildOutputFolder) + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + diff --git a/src/HealthChecks.AzureServiceBus/AzureEventHubHealthCheck.cs b/src/HealthChecks.AzureServiceBus/AzureEventHubHealthCheck.cs index 29c569bd3f..988f5fde16 100644 --- a/src/HealthChecks.AzureServiceBus/AzureEventHubHealthCheck.cs +++ b/src/HealthChecks.AzureServiceBus/AzureEventHubHealthCheck.cs @@ -13,8 +13,18 @@ public class AzureEventHubHealthCheck private readonly string _eventHubName; public AzureEventHubHealthCheck(string connectionString, string eventHubName) { - _connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString)); - _eventHubName = eventHubName ?? throw new ArgumentNullException(nameof(eventHubName)); + if (string.IsNullOrEmpty(connectionString)) + { + throw new ArgumentNullException(nameof(connectionString)); + } + + if (string.IsNullOrEmpty(eventHubName)) + { + throw new ArgumentNullException(nameof(eventHubName)); + } + + _connectionString = connectionString; + _eventHubName = eventHubName; } public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { diff --git a/src/HealthChecks.AzureServiceBus/AzureServiceBusQueueHealthCheck.cs b/src/HealthChecks.AzureServiceBus/AzureServiceBusQueueHealthCheck.cs index aab5e040a3..4c47ee06c5 100644 --- a/src/HealthChecks.AzureServiceBus/AzureServiceBusQueueHealthCheck.cs +++ b/src/HealthChecks.AzureServiceBus/AzureServiceBusQueueHealthCheck.cs @@ -15,8 +15,11 @@ public class AzureServiceBusQueueHealthCheck private readonly string _queueName; public AzureServiceBusQueueHealthCheck(string connectionString, string queueName) { - _connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString)); - _queueName = queueName ?? throw new ArgumentNullException(nameof(queueName)); + if (string.IsNullOrEmpty(connectionString)) throw new ArgumentNullException(nameof(connectionString)); + if (string.IsNullOrEmpty(queueName)) throw new ArgumentNullException(nameof(queueName)); + + _connectionString = connectionString; + _queueName = queueName; } public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { diff --git a/src/HealthChecks.AzureServiceBus/AzureServiceBusTopicHealthCheck.cs b/src/HealthChecks.AzureServiceBus/AzureServiceBusTopicHealthCheck.cs index b9bc1a87b8..9083af223d 100644 --- a/src/HealthChecks.AzureServiceBus/AzureServiceBusTopicHealthCheck.cs +++ b/src/HealthChecks.AzureServiceBus/AzureServiceBusTopicHealthCheck.cs @@ -15,8 +15,11 @@ public class AzureServiceBusTopicHealthCheck private readonly string _topicName; public AzureServiceBusTopicHealthCheck(string connectionString, string topicName) { - _connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString)); - _topicName = topicName ?? throw new ArgumentNullException(nameof(topicName)); + if (string.IsNullOrEmpty(connectionString)) throw new ArgumentNullException(nameof(connectionString)); + if (string.IsNullOrEmpty(topicName)) throw new ArgumentNullException(nameof(topicName)); + + _connectionString = connectionString; + _topicName = topicName; } public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { diff --git a/test/UnitTests/DependencyInjection/HealthChecks.AzureKeyVault/AzureKeyVaultUnitTests.cs b/test/UnitTests/DependencyInjection/HealthChecks.AzureKeyVault/AzureKeyVaultUnitTests.cs new file mode 100644 index 0000000000..4095005b7d --- /dev/null +++ b/test/UnitTests/DependencyInjection/HealthChecks.AzureKeyVault/AzureKeyVaultUnitTests.cs @@ -0,0 +1,97 @@ +using FluentAssertions; +using HealthChecks.AzureKeyVault; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Xunit; + +namespace UnitTests.HealthChecks.DependencyInjection.AzureKeyVault +{ + public class azure_keyvault_registration_should + { + [Fact] + public void add_health_check_when_properly_configured() + { + var services = new ServiceCollection(); + services.AddHealthChecks() + .AddAzureKeyVault(setup => + { + setup + .UseKeyVaultUrl("https://keyvault") + .AddSecret("supercret"); + }); + + var serviceProvider = services.BuildServiceProvider(); + var options = serviceProvider.GetService>(); + + var registration = options.Value.Registrations.First(); + var check = registration.Factory(serviceProvider); + + registration.Name.Should().Be("azurekeyvault"); + check.GetType().Should().Be(typeof(AzureKeyVaultHealthCheck)); + + } + + [Fact] + public void add_named_health_check_when_properly_configured() + { + var services = new ServiceCollection(); + services.AddHealthChecks() + .AddAzureKeyVault(setup => + { + setup + .UseKeyVaultUrl("https://keyvault") + .UseClientSecrets("client", "secret"); + + }, name: "keyvaultcheck"); + + var serviceProvider = services.BuildServiceProvider(); + var options = serviceProvider.GetService>(); + + var registration = options.Value.Registrations.First(); + var check = registration.Factory(serviceProvider); + + registration.Name.Should().Be("keyvaultcheck"); + check.GetType().Should().Be(typeof(AzureKeyVaultHealthCheck)); + } + + [Fact] + public void fail_when_invalidad_uri_provided_in_configuration() + { + var services = new ServiceCollection(); + services.AddHealthChecks() + .AddAzureKeyVault(setup => + { + setup + .UseKeyVaultUrl("invalid URI") + .AddSecret("mysecret"); + }); + + var serviceProvider = services.BuildServiceProvider(); + var options = serviceProvider.GetService>(); + + var registration = options.Value.Registrations.First(); + + Assert.Throws(() => registration.Factory(serviceProvider)); + } + + [Fact] + public void fail_when_no_health_check_configuration_provided() + { + var services = new ServiceCollection(); + services.AddHealthChecks() + .AddAzureKeyVault(setup => { }); + + var serviceProvider = services.BuildServiceProvider(); + var options = serviceProvider.GetService>(); + + var registration = options.Value.Registrations.First(); + + Assert.Throws(() => registration.Factory(serviceProvider)); + } + } +} diff --git a/test/UnitTests/DependencyInjection/HeathChecks.AzureServiceBus/AzureEventHubUnitTests.cs b/test/UnitTests/DependencyInjection/HeathChecks.AzureServiceBus/AzureEventHubUnitTests.cs new file mode 100644 index 0000000000..2445d00a71 --- /dev/null +++ b/test/UnitTests/DependencyInjection/HeathChecks.AzureServiceBus/AzureEventHubUnitTests.cs @@ -0,0 +1,66 @@ +using FluentAssertions; +using HealthChecks.AzureServiceBus; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Xunit; + +namespace UnitTests.HeathChecks.DependencyInjection.AzureServiceBus +{ + public class azure_event_hub_registration_should + { + [Fact] + public void add_health_check_when_properly_configured() + { + var services = new ServiceCollection(); + services.AddHealthChecks() + .AddAzureEventHub("cnn", "hubName"); + + var serviceProvider = services.BuildServiceProvider(); + var options = serviceProvider.GetService>(); + + var registration = options.Value.Registrations.First(); + var check = registration.Factory(serviceProvider); + + registration.Name.Should().Be("azureeventhub"); + check.GetType().Should().Be(typeof(AzureEventHubHealthCheck)); + } + + [Fact] + public void add_named_health_check_when_properly_configured() + { + var services = new ServiceCollection(); + services.AddHealthChecks() + .AddAzureEventHub("cnn", "hubName", + name: "azureeventhubcheck"); + + var serviceProvider = services.BuildServiceProvider(); + var options = serviceProvider.GetService>(); + + var registration = options.Value.Registrations.First(); + var check = registration.Factory(serviceProvider); + + registration.Name.Should().Be("azureeventhubcheck"); + check.GetType().Should().Be(typeof(AzureEventHubHealthCheck)); + } + + [Fact] + public void fail_when_no_health_check_configuration_provided() + { + var services = new ServiceCollection(); + services.AddHealthChecks() + .AddAzureEventHub(string.Empty, string.Empty); + + var serviceProvider = services.BuildServiceProvider(); + var options = serviceProvider.GetService>(); + + var registration = options.Value.Registrations.First(); + + Assert.Throws(() => registration.Factory(serviceProvider)); + } + } +} diff --git a/test/UnitTests/DependencyInjection/HeathChecks.AzureServiceBus/AzureServiceBusQueueUnitTests.cs b/test/UnitTests/DependencyInjection/HeathChecks.AzureServiceBus/AzureServiceBusQueueUnitTests.cs new file mode 100644 index 0000000000..2ff631b98d --- /dev/null +++ b/test/UnitTests/DependencyInjection/HeathChecks.AzureServiceBus/AzureServiceBusQueueUnitTests.cs @@ -0,0 +1,67 @@ +using FluentAssertions; +using HealthChecks.AzureServiceBus; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Xunit; + +namespace UnitTests.HeathChecks.DependencyInjection.AzureServiceBus +{ + public class azure_service_bus_queue_registration_should + { + [Fact] + public void add_health_check_when_properly_configured() + { + var services = new ServiceCollection(); + services.AddHealthChecks() + .AddAzureServiceBusQueue("cnn", "queueName"); + + var serviceProvider = services.BuildServiceProvider(); + var options = serviceProvider.GetService>(); + + var registration = options.Value.Registrations.First(); + var check = registration.Factory(serviceProvider); + + registration.Name.Should().Be("azurequeue"); + check.GetType().Should().Be(typeof(AzureServiceBusQueueHealthCheck)); + + } + + [Fact] + public void add_named_health_check_when_properly_configured() + { + var services = new ServiceCollection(); + services.AddHealthChecks() + .AddAzureServiceBusQueue("cnn", "queueName", + name: "azureservicebusqueuecheck"); + + var serviceProvider = services.BuildServiceProvider(); + var options = serviceProvider.GetService>(); + + var registration = options.Value.Registrations.First(); + var check = registration.Factory(serviceProvider); + + registration.Name.Should().Be("azureservicebusqueuecheck"); + check.GetType().Should().Be(typeof(AzureServiceBusQueueHealthCheck)); + } + + [Fact] + public void fail_when_no_health_check_configuration_provided() + { + var services = new ServiceCollection(); + services.AddHealthChecks() + .AddAzureServiceBusQueue(string.Empty, string.Empty); + + var serviceProvider = services.BuildServiceProvider(); + var options = serviceProvider.GetService>(); + + var registration = options.Value.Registrations.First(); + + Assert.Throws(() => registration.Factory(serviceProvider)); + } + } +} diff --git a/test/UnitTests/DependencyInjection/HeathChecks.AzureServiceBus/AzureServiceBusTopicUnitTests.cs b/test/UnitTests/DependencyInjection/HeathChecks.AzureServiceBus/AzureServiceBusTopicUnitTests.cs new file mode 100644 index 0000000000..d0246c800e --- /dev/null +++ b/test/UnitTests/DependencyInjection/HeathChecks.AzureServiceBus/AzureServiceBusTopicUnitTests.cs @@ -0,0 +1,66 @@ +using FluentAssertions; +using HealthChecks.AzureServiceBus; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Xunit; + +namespace UnitTests.HeathChecks.DependencyInjection.AzureServiceBus +{ + public class azure_service_bus_topic_registration_should + { + [Fact] + public void add_health_check_when_properly_configured() + { + var services = new ServiceCollection(); + services.AddHealthChecks() + .AddAzureServiceBusTopic("cnn", "topicName"); + + var serviceProvider = services.BuildServiceProvider(); + var options = serviceProvider.GetService>(); + + var registration = options.Value.Registrations.First(); + var check = registration.Factory(serviceProvider); + + registration.Name.Should().Be("azuretopic"); + check.GetType().Should().Be(typeof(AzureServiceBusTopicHealthCheck)); + } + + [Fact] + public void add_named_health_check_when_properly_configured() + { + var services = new ServiceCollection(); + services.AddHealthChecks() + .AddAzureServiceBusTopic("cnn", "topic", + name: "azuretopiccheck"); + + var serviceProvider = services.BuildServiceProvider(); + var options = serviceProvider.GetService>(); + + var registration = options.Value.Registrations.First(); + var check = registration.Factory(serviceProvider); + + registration.Name.Should().Be("azuretopiccheck"); + check.GetType().Should().Be(typeof(AzureServiceBusTopicHealthCheck)); + } + + [Fact] + public void fail_when_no_health_check_configuration_provided() + { + var services = new ServiceCollection(); + services.AddHealthChecks() + .AddAzureServiceBusTopic(string.Empty, string.Empty); + + var serviceProvider = services.BuildServiceProvider(); + var options = serviceProvider.GetService>(); + + var registration = options.Value.Registrations.First(); + + Assert.Throws(() => registration.Factory(serviceProvider)); + } + } +} diff --git a/test/UnitTests/UnitTests.csproj b/test/UnitTests/UnitTests.csproj new file mode 100644 index 0000000000..c5edd0d8ca --- /dev/null +++ b/test/UnitTests/UnitTests.csproj @@ -0,0 +1,23 @@ + + + + $(NetCoreTargetVersion) + + false + + + + + + + + + + + + + + + + +