diff --git a/samples/HealthChecksSample/DbConnectionHealthCheck.cs b/samples/HealthChecksSample/DbConnectionHealthCheck.cs index 5a0ddcdd55..cb865f67c0 100644 --- a/samples/HealthChecksSample/DbConnectionHealthCheck.cs +++ b/samples/HealthChecksSample/DbConnectionHealthCheck.cs @@ -51,11 +51,11 @@ namespace HealthChecksSample } catch (DbException ex) { - return HealthCheckResult.Failed(exception: ex); + return new HealthCheckResult(status: context.Registration.FailureStatus, exception: ex); } } - return HealthCheckResult.Passed(); + return HealthCheckResult.Healthy(); } } } diff --git a/samples/HealthChecksSample/GCInfoHealthCheck.cs b/samples/HealthChecksSample/GCInfoHealthCheck.cs index c708f3ed3c..91519af452 100644 --- a/samples/HealthChecksSample/GCInfoHealthCheck.cs +++ b/samples/HealthChecksSample/GCInfoHealthCheck.cs @@ -65,13 +65,15 @@ namespace HealthChecksSample { "Gen2Collections", GC.CollectionCount(2) }, }; - // Report failure if the allocated memory is >= the threshold. Negated because true == success - var result = !(allocated >= options.Threshold); + // Report failure if the allocated memory is >= the threshold. + // + // Using context.Registration.FailureStatus means that the application developer can configure + // how they want failures to appear. + var result = allocated >= options.Threshold ? context.Registration.FailureStatus : HealthStatus.Healthy; return Task.FromResult(new HealthCheckResult( result, description: "reports degraded status if allocated bytes >= 1gb", - exception: null, data: data)); } } diff --git a/samples/HealthChecksSample/SlowDependencyHealthCheck.cs b/samples/HealthChecksSample/SlowDependencyHealthCheck.cs index e319952cf0..e14aeb210c 100644 --- a/samples/HealthChecksSample/SlowDependencyHealthCheck.cs +++ b/samples/HealthChecksSample/SlowDependencyHealthCheck.cs @@ -21,10 +21,12 @@ namespace HealthChecksSample { if (_task.IsCompleted) { - return Task.FromResult(HealthCheckResult.Passed("Dependency is ready")); + return Task.FromResult(HealthCheckResult.Healthy("Dependency is ready")); } - return Task.FromResult(HealthCheckResult.Failed("Dependency is still initializing")); + return Task.FromResult(new HealthCheckResult( + status: context.Registration.FailureStatus, + description: "Dependency is still initializing")); } } } diff --git a/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/HealthCheckOptions.cs b/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/HealthCheckOptions.cs index bf43676a5c..16b81c2b96 100644 --- a/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/HealthCheckOptions.cs +++ b/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/HealthCheckOptions.cs @@ -33,9 +33,6 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks { HealthStatus.Healthy, StatusCodes.Status200OK }, { HealthStatus.Degraded, StatusCodes.Status200OK }, { HealthStatus.Unhealthy, StatusCodes.Status503ServiceUnavailable }, - - // This means that a health check failed, so 500 is appropriate. This is an error. - { HealthStatus.Failed, StatusCodes.Status500InternalServerError }, }; /// diff --git a/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/HealthCheckResult.cs b/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/HealthCheckResult.cs index 12cd38d08e..e01cb5aceb 100644 --- a/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/HealthCheckResult.cs +++ b/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/HealthCheckResult.cs @@ -14,16 +14,16 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks private static readonly IReadOnlyDictionary _emptyReadOnlyDictionary = new Dictionary(); /// - /// Creates a new with the specified values for , , - /// , and . + /// Creates a new with the specified values for , + /// , , and . /// - /// A value indicating the pass/fail status of the component that was checked. + /// A value indicating the status of the component that was checked. /// A human-readable description of the status of the component that was checked. /// An representing the exception that was thrown when checking for status (if any). /// Additional key-value pairs describing the health of the component. - public HealthCheckResult(bool result, string description, Exception exception, IReadOnlyDictionary data) + public HealthCheckResult(HealthStatus status, string description = null, Exception exception = null, IReadOnlyDictionary data = null) { - Result = result; + Status = status; Description = description; Exception = exception; Data = data ?? _emptyReadOnlyDictionary; @@ -45,33 +45,44 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks public Exception Exception { get; } /// - /// Gets a value indicating the pass/fail status of the component that was checked. If true, then the component - /// is considered to have passed health validation. A false value will be mapped to the configured - /// by the health check system. + /// Gets a value indicating the status of the component that was checked. /// - public bool Result { get; } + public HealthStatus Status { get; } /// - /// Creates a representing a passing component. + /// Creates a representing a healthy component. /// - /// A representing a passing component. /// A human-readable description of the status of the component that was checked. Optional. /// Additional key-value pairs describing the health of the component. Optional. - public static HealthCheckResult Passed(string description = null, IReadOnlyDictionary data = null) + /// A representing a healthy component. + public static HealthCheckResult Healthy(string description = null, IReadOnlyDictionary data = null) { - return new HealthCheckResult(result: true, description, exception: null, data); + return new HealthCheckResult(status: HealthStatus.Healthy, description, exception: null, data); } + /// - /// Creates a representing an failing component. + /// Creates a representing a degraded component. /// /// A human-readable description of the status of the component that was checked. Optional. /// An representing the exception that was thrown when checking for status. Optional. /// Additional key-value pairs describing the health of the component. Optional. - /// A representing an failing component. - public static HealthCheckResult Failed(string description = null, Exception exception = null, IReadOnlyDictionary data = null) + /// A representing a degraged component. + public static HealthCheckResult Degraded(string description = null, Exception exception = null, IReadOnlyDictionary data = null) { - return new HealthCheckResult(result: false, description, exception, data); + return new HealthCheckResult(status: HealthStatus.Degraded, description, exception: null, data); + } + + /// + /// Creates a representing an unhealthy component. + /// + /// A human-readable description of the status of the component that was checked. Optional. + /// An representing the exception that was thrown when checking for status. Optional. + /// Additional key-value pairs describing the health of the component. Optional. + /// A representing an unhealthy component. + public static HealthCheckResult Unhealthy(string description = null, Exception exception = null, IReadOnlyDictionary data = null) + { + return new HealthCheckResult(status: HealthStatus.Unhealthy, description, exception, data); } } } diff --git a/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/HealthReport.cs b/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/HealthReport.cs index a0e6fac1cd..91ed798811 100644 --- a/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/HealthReport.cs +++ b/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/HealthReport.cs @@ -54,7 +54,7 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks currentValue = entry.Status; } - if (currentValue == HealthStatus.Failed) + if (currentValue == HealthStatus.Unhealthy) { // Game over, man! Game over! // (We hit the worst possible status, so there's no need to keep iterating) diff --git a/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/HealthReportEntry.cs b/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/HealthReportEntry.cs index 2898c93aa4..6e7d6c6b8e 100644 --- a/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/HealthReportEntry.cs +++ b/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/HealthReportEntry.cs @@ -52,8 +52,7 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks public Exception Exception { get; } /// - /// Gets the health status of the component that was checked. The is based on the pass/fail value of - /// and the configured value of . + /// Gets the health status of the component that was checked. /// public HealthStatus Status { get; } } diff --git a/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/HealthStatus.cs b/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/HealthStatus.cs index ad3f40a201..61b76d54fa 100644 --- a/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/HealthStatus.cs +++ b/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/HealthStatus.cs @@ -8,10 +8,6 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks /// /// /// - /// A health status is derived the pass/fail result of an () - /// and the corresponding value of . - /// - /// /// A status of should be considered the default value for a failing health check. Application /// developers may configure a health check to report a different status as desired. /// @@ -23,23 +19,19 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks public enum HealthStatus { /// - /// Indicates that an unexpected exception was thrown when running the health check. + /// Indicates that the health check determined that the component was unhealthy, or an unhandled + /// exception was thrown while executing the health check. /// - Failed = 0, - - /// - /// Indicates that the health check determined that the component was unhealthy. - /// - Unhealthy = 1, + Unhealthy = 0, /// /// Indicates that the health check determined that the component was in a degraded state. /// - Degraded = 2, + Degraded = 1, /// /// Indicates that the health check determined that the component was healthy. /// - Healthy = 3, + Healthy = 2, } } diff --git a/src/Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore/DbContextHealthCheck.cs b/src/Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore/DbContextHealthCheck.cs index 9d581e89d3..7fa998f296 100644 --- a/src/Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore/DbContextHealthCheck.cs +++ b/src/Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore/DbContextHealthCheck.cs @@ -47,10 +47,10 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks if (await testQuery(_dbContext, cancellationToken)) { - return HealthCheckResult.Passed(); + return HealthCheckResult.Healthy(); } - return HealthCheckResult.Failed(); + return HealthCheckResult.Unhealthy(); } } } diff --git a/src/Microsoft.Extensions.Diagnostics.HealthChecks/DefaultHealthCheckService.cs b/src/Microsoft.Extensions.Diagnostics.HealthChecks/DefaultHealthCheckService.cs index aa12d97418..d5d71d9cb4 100644 --- a/src/Microsoft.Extensions.Diagnostics.HealthChecks/DefaultHealthCheckService.cs +++ b/src/Microsoft.Extensions.Diagnostics.HealthChecks/DefaultHealthCheckService.cs @@ -76,7 +76,7 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks var duration = stopwatch.GetElapsedTime(); entry = new HealthReportEntry( - status: result.Result ? HealthStatus.Healthy : registration.FailureStatus, + status: result.Status, description: result.Description, duration: duration, exception: result.Exception, @@ -91,7 +91,7 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks { var duration = stopwatch.GetElapsedTime(); entry = new HealthReportEntry( - status: HealthStatus.Failed, + status: HealthStatus.Unhealthy, description: ex.Message, duration: duration, exception: ex, @@ -212,10 +212,6 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks case HealthStatus.Unhealthy: _healthCheckEndUnhealthy(logger, registration.Name, duration.TotalMilliseconds, entry.Status, entry.Description, null); break; - - case HealthStatus.Failed: - _healthCheckEndFailed(logger, registration.Name, duration.TotalMilliseconds, entry.Status, entry.Description, null); - break; } } diff --git a/src/Microsoft.Extensions.Diagnostics.HealthChecks/DependencyInjection/HealthChecksBuilderDelegateExtensions.cs b/src/Microsoft.Extensions.Diagnostics.HealthChecks/DependencyInjection/HealthChecksBuilderDelegateExtensions.cs index 70edd0649d..d7dfdd90ae 100644 --- a/src/Microsoft.Extensions.Diagnostics.HealthChecks/DependencyInjection/HealthChecksBuilderDelegateExtensions.cs +++ b/src/Microsoft.Extensions.Diagnostics.HealthChecks/DependencyInjection/HealthChecksBuilderDelegateExtensions.cs @@ -19,10 +19,6 @@ namespace Microsoft.Extensions.DependencyInjection /// /// The . /// The name of the health check. - /// - /// The that should be reported when the health check reports a failure. If the provided value - /// is null, then will be reported. - /// /// A list of tags that can be used to filter health checks. /// A delegate that provides the health check implementation. /// The . @@ -30,7 +26,6 @@ namespace Microsoft.Extensions.DependencyInjection this IHealthChecksBuilder builder, string name, Func check, - HealthStatus? failureStatus = null, IEnumerable tags = null) { if (builder == null) @@ -49,7 +44,7 @@ namespace Microsoft.Extensions.DependencyInjection } var instance = new DelegateHealthCheck((ct) => Task.FromResult(check())); - return builder.Add(new HealthCheckRegistration(name, instance, failureStatus, tags)); + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags)); } /// @@ -57,10 +52,6 @@ namespace Microsoft.Extensions.DependencyInjection /// /// The . /// The name of the health check. - /// - /// The that should be reported when the health check reports a failure. If the provided value - /// is null, then will be reported. - /// /// A list of tags that can be used to filter health checks. /// A delegate that provides the health check implementation. /// The . @@ -68,7 +59,6 @@ namespace Microsoft.Extensions.DependencyInjection this IHealthChecksBuilder builder, string name, Func check, - HealthStatus? failureStatus = null, IEnumerable tags = null) { if (builder == null) @@ -87,7 +77,7 @@ namespace Microsoft.Extensions.DependencyInjection } var instance = new DelegateHealthCheck((ct) => Task.FromResult(check(ct))); - return builder.Add(new HealthCheckRegistration(name, instance, failureStatus, tags)); + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags)); } /// @@ -95,10 +85,6 @@ namespace Microsoft.Extensions.DependencyInjection /// /// The . /// The name of the health check. - /// - /// The that should be reported when the health check reports a failure. If the provided value - /// is null, then will be reported. - /// /// A list of tags that can be used to filter health checks. /// A delegate that provides the health check implementation. /// The . @@ -106,7 +92,6 @@ namespace Microsoft.Extensions.DependencyInjection this IHealthChecksBuilder builder, string name, Func> check, - HealthStatus? failureStatus = null, IEnumerable tags = null) { if (builder == null) @@ -125,7 +110,7 @@ namespace Microsoft.Extensions.DependencyInjection } var instance = new DelegateHealthCheck((ct) => check()); - return builder.Add(new HealthCheckRegistration(name, instance, failureStatus, tags)); + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags)); } /// @@ -133,10 +118,6 @@ namespace Microsoft.Extensions.DependencyInjection /// /// The . /// The name of the health check. - /// - /// The that should be reported when the health check reports a failure. If the provided value - /// is null, then will be reported. - /// /// A list of tags that can be used to filter health checks. /// A delegate that provides the health check implementation. /// The . @@ -144,7 +125,6 @@ namespace Microsoft.Extensions.DependencyInjection this IHealthChecksBuilder builder, string name, Func> check, - HealthStatus? failureStatus = null, IEnumerable tags = null) { if (builder == null) @@ -163,7 +143,7 @@ namespace Microsoft.Extensions.DependencyInjection } var instance = new DelegateHealthCheck((ct) => check(ct)); - return builder.Add(new HealthCheckRegistration(name, instance, failureStatus, tags)); + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags)); } } } diff --git a/test/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests/HealthCheckMiddlewareTests.cs b/test/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests/HealthCheckMiddlewareTests.cs index a3036f9b70..9bcefafbaa 100644 --- a/test/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests/HealthCheckMiddlewareTests.cs +++ b/test/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests/HealthCheckMiddlewareTests.cs @@ -110,9 +110,9 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks .ConfigureServices(services => { services.AddHealthChecks() - .AddCheck("Foo", () => HealthCheckResult.Passed("A-ok!")) - .AddCheck("Bar", () => HealthCheckResult.Passed("A-ok!")) - .AddCheck("Baz", () => HealthCheckResult.Passed("A-ok!")); + .AddCheck("Foo", () => HealthCheckResult.Healthy("A-ok!")) + .AddCheck("Bar", () => HealthCheckResult.Healthy("A-ok!")) + .AddCheck("Baz", () => HealthCheckResult.Healthy("A-ok!")); }); var server = new TestServer(builder); var client = server.CreateClient(); @@ -135,9 +135,9 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks .ConfigureServices(services => { services.AddHealthChecks() - .AddCheck("Foo", () => HealthCheckResult.Passed("A-ok!")) - .AddCheck("Bar", () => HealthCheckResult.Failed("Not so great."), failureStatus: HealthStatus.Degraded) - .AddCheck("Baz", () => HealthCheckResult.Passed("A-ok!")); + .AddCheck("Foo", () => HealthCheckResult.Healthy("A-ok!")) + .AddCheck("Bar", () => HealthCheckResult.Degraded("Not so great.")) + .AddCheck("Baz", () => HealthCheckResult.Healthy("A-ok!")); }); var server = new TestServer(builder); var client = server.CreateClient(); @@ -160,9 +160,9 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks .ConfigureServices(services => { services.AddHealthChecks() - .AddAsyncCheck("Foo", () => Task.FromResult(HealthCheckResult.Passed("A-ok!"))) - .AddAsyncCheck("Bar", () => Task.FromResult(HealthCheckResult.Failed("Pretty bad."))) - .AddAsyncCheck("Baz", () => Task.FromResult(HealthCheckResult.Passed("A-ok!"))); + .AddAsyncCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!"))) + .AddAsyncCheck("Bar", () => Task.FromResult(HealthCheckResult.Unhealthy("Pretty bad."))) + .AddAsyncCheck("Baz", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!"))); }); var server = new TestServer(builder); var client = server.CreateClient(); @@ -175,7 +175,7 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks } [Fact] - public async Task StatusCodeIs500IfCheckIsFailed() + public async Task StatusCodeIs503IfCheckHasUnhandledException() { var builder = new WebHostBuilder() .Configure(app => @@ -185,18 +185,18 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks .ConfigureServices(services => { services.AddHealthChecks() - .AddAsyncCheck("Foo", () => Task.FromResult(HealthCheckResult.Passed("A-ok!"))) + .AddAsyncCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!"))) .AddAsyncCheck("Bar", () => throw null) - .AddAsyncCheck("Baz", () => Task.FromResult(HealthCheckResult.Passed("A-ok!"))); + .AddAsyncCheck("Baz", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!"))); }); var server = new TestServer(builder); var client = server.CreateClient(); var response = await client.GetAsync("/health"); - Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode); Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString()); - Assert.Equal("Failed", await response.Content.ReadAsStringAsync()); + Assert.Equal("Unhealthy", await response.Content.ReadAsStringAsync()); } [Fact] @@ -223,9 +223,9 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks .ConfigureServices(services => { services.AddHealthChecks() - .AddAsyncCheck("Foo", () => Task.FromResult(HealthCheckResult.Passed("A-ok!"))) - .AddAsyncCheck("Bar", () => Task.FromResult(HealthCheckResult.Failed("Pretty bad."))) - .AddAsyncCheck("Baz", () => Task.FromResult(HealthCheckResult.Passed("A-ok!"))); + .AddAsyncCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!"))) + .AddAsyncCheck("Bar", () => Task.FromResult(HealthCheckResult.Unhealthy("Pretty bad."))) + .AddAsyncCheck("Baz", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!"))); }); var server = new TestServer(builder); var client = server.CreateClient(); @@ -252,9 +252,9 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks .ConfigureServices(services => { services.AddHealthChecks() - .AddAsyncCheck("Foo", () => Task.FromResult(HealthCheckResult.Passed("A-ok!"))) - .AddAsyncCheck("Bar", () => Task.FromResult(HealthCheckResult.Failed("Pretty bad."))) - .AddAsyncCheck("Baz", () => Task.FromResult(HealthCheckResult.Passed("A-ok!"))); + .AddAsyncCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!"))) + .AddAsyncCheck("Bar", () => Task.FromResult(HealthCheckResult.Unhealthy("Pretty bad."))) + .AddAsyncCheck("Baz", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!"))); }); var server = new TestServer(builder); var client = server.CreateClient(); @@ -357,10 +357,10 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks .ConfigureServices(services => { services.AddHealthChecks() - .AddAsyncCheck("Foo", () => Task.FromResult(HealthCheckResult.Passed("A-ok!"))) + .AddAsyncCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!"))) // Will get filtered out - .AddAsyncCheck("Bar", () => Task.FromResult(HealthCheckResult.Failed("A-ok!"))) - .AddAsyncCheck("Baz", () => Task.FromResult(HealthCheckResult.Passed("A-ok!"))); + .AddAsyncCheck("Bar", () => Task.FromResult(HealthCheckResult.Unhealthy("A-ok!"))) + .AddAsyncCheck("Baz", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!"))); }); var server = new TestServer(builder); var client = server.CreateClient(); diff --git a/test/Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.Tests/DbContextHealthCheckTest.cs b/test/Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.Tests/DbContextHealthCheckTest.cs index 23b17d4939..b564a62795 100644 --- a/test/Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.Tests/DbContextHealthCheckTest.cs +++ b/test/Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.Tests/DbContextHealthCheckTest.cs @@ -14,9 +14,9 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks { public class DbContextHealthCheckTest { - // Just testing pass here since it would be complicated to simulate a failure. All of that logic lives in EF anyway. + // Just testing healthy here since it would be complicated to simulate a failure. All of that logic lives in EF anyway. [Fact] - public async Task CheckAsync_DefaultTest_Pass() + public async Task CheckAsync_DefaultTest_Healthy() { // Arrange var services = CreateServices(); @@ -29,12 +29,12 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks var result = await check.CheckHealthAsync(new HealthCheckContext() { Registration = registration, }); // Assert - Assert.True(result.Result, "Health check passed"); + Assert.Equal(HealthStatus.Healthy, result.Status); } } [Fact] - public async Task CheckAsync_CustomTest_Pass() + public async Task CheckAsync_CustomTest_Healthy() { // Arrange var services = CreateServices(async (c, ct) => @@ -56,19 +56,18 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks var result = await check.CheckHealthAsync(new HealthCheckContext() { Registration = registration, }); // Assert - Assert.True(result.Result, "Health check passed"); + Assert.Equal(HealthStatus.Healthy, result.Status); } } - [Fact] - public async Task CheckAsync_CustomTest_Fail() + public async Task CheckAsync_CustomTest_Degraded() { // Arrange var services = CreateServices(async (c, ct) => { return 0 < await c.Blogs.CountAsync(); - }); + }, failureStatus: HealthStatus.Degraded); using (var scope = services.GetRequiredService().CreateScope()) { @@ -79,17 +78,41 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks var result = await check.CheckHealthAsync(new HealthCheckContext() { Registration = registration, }); // Assert - Assert.False(result.Result, "Health check failed"); + Assert.Equal(HealthStatus.Unhealthy, result.Status); } } - private static IServiceProvider CreateServices(Func> testQuery = null) + [Fact] + public async Task CheckAsync_CustomTest_Unhealthy() + { + // Arrange + var services = CreateServices(async (c, ct) => + { + return 0 < await c.Blogs.CountAsync(); + }, failureStatus: HealthStatus.Unhealthy); + + using (var scope = services.GetRequiredService().CreateScope()) + { + var registration = Assert.Single(services.GetRequiredService>().Value.Registrations); + var check = ActivatorUtilities.CreateInstance>(scope.ServiceProvider); + + // Act + var result = await check.CheckHealthAsync(new HealthCheckContext() { Registration = registration, }); + + // Assert + Assert.Equal(HealthStatus.Unhealthy, result.Status); + } + } + + private static IServiceProvider CreateServices( + Func> testQuery = null, + HealthStatus failureStatus = HealthStatus.Unhealthy) { var serviceCollection = new ServiceCollection(); serviceCollection.AddDbContext(o => o.UseInMemoryDatabase("Test")); var builder = serviceCollection.AddHealthChecks(); - builder.AddDbContextCheck("test", HealthStatus.Degraded, new[] { "tag1", "tag2", }, testQuery); + builder.AddDbContextCheck("test", failureStatus, new[] { "tag1", "tag2", }, testQuery); return serviceCollection.BuildServiceProvider(); } } diff --git a/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/DefaultHealthCheckServiceTest.cs b/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/DefaultHealthCheckServiceTest.cs index 6c74ddcdf0..9ab991204e 100644 --- a/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/DefaultHealthCheckServiceTest.cs +++ b/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/DefaultHealthCheckServiceTest.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; @@ -26,11 +27,11 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks serviceCollection.AddLogging(); serviceCollection.AddOptions(); serviceCollection.AddHealthChecks() - .AddCheck("Foo", new DelegateHealthCheck(_ => Task.FromResult(HealthCheckResult.Passed()))) - .AddCheck("Foo", new DelegateHealthCheck(_ => Task.FromResult(HealthCheckResult.Passed()))) - .AddCheck("Bar", new DelegateHealthCheck(_ => Task.FromResult(HealthCheckResult.Passed()))) - .AddCheck("Baz", new DelegateHealthCheck(_ => Task.FromResult(HealthCheckResult.Passed()))) - .AddCheck("Baz", new DelegateHealthCheck(_ => Task.FromResult(HealthCheckResult.Passed()))); + .AddCheck("Foo", new DelegateHealthCheck(_ => Task.FromResult(HealthCheckResult.Healthy()))) + .AddCheck("Foo", new DelegateHealthCheck(_ => Task.FromResult(HealthCheckResult.Healthy()))) + .AddCheck("Bar", new DelegateHealthCheck(_ => Task.FromResult(HealthCheckResult.Healthy()))) + .AddCheck("Baz", new DelegateHealthCheck(_ => Task.FromResult(HealthCheckResult.Healthy()))) + .AddCheck("Baz", new DelegateHealthCheck(_ => Task.FromResult(HealthCheckResult.Healthy()))); var services = serviceCollection.BuildServiceProvider(); @@ -63,16 +64,25 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks var service = CreateHealthChecksService(b => { - b.AddAsyncCheck("HealthyCheck", _ => Task.FromResult(HealthCheckResult.Passed(HealthyMessage, data))); - b.AddAsyncCheck("DegradedCheck", _ => Task.FromResult(HealthCheckResult.Failed(DegradedMessage)), failureStatus: HealthStatus.Degraded); - b.AddAsyncCheck("UnhealthyCheck", _ => Task.FromResult(HealthCheckResult.Failed(UnhealthyMessage, exception))); + b.AddAsyncCheck("HealthyCheck", _ => Task.FromResult(HealthCheckResult.Healthy(HealthyMessage, data))); + b.AddAsyncCheck("DegradedCheck", _ => Task.FromResult(HealthCheckResult.Degraded(DegradedMessage))); + b.AddAsyncCheck("UnhealthyCheck", _ => Task.FromResult(HealthCheckResult.Unhealthy(UnhealthyMessage, exception))); }); // Act var results = await service.CheckHealthAsync(); // Assert - Assert.Collection(results.Entries, + Assert.Collection( + results.Entries.OrderBy(kvp => kvp.Key), + actual => + { + Assert.Equal("DegradedCheck", actual.Key); + Assert.Equal(DegradedMessage, actual.Value.Description); + Assert.Equal(HealthStatus.Degraded, actual.Value.Status); + Assert.Null(actual.Value.Exception); + Assert.Empty(actual.Value.Data); + }, actual => { Assert.Equal("HealthyCheck", actual.Key); @@ -86,14 +96,6 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks }); }, actual => - { - Assert.Equal("DegradedCheck", actual.Key); - Assert.Equal(DegradedMessage, actual.Value.Description); - Assert.Equal(HealthStatus.Degraded, actual.Value.Status); - Assert.Null(actual.Value.Exception); - Assert.Empty(actual.Value.Data); - }, - actual => { Assert.Equal("UnhealthyCheck", actual.Key); Assert.Equal(UnhealthyMessage, actual.Value.Description); @@ -121,9 +123,9 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks var service = CreateHealthChecksService(b => { - b.AddAsyncCheck("HealthyCheck", _ => Task.FromResult(HealthCheckResult.Passed(HealthyMessage, data))); - b.AddAsyncCheck("DegradedCheck", _ => Task.FromResult(HealthCheckResult.Failed(DegradedMessage)), failureStatus: HealthStatus.Degraded); - b.AddAsyncCheck("UnhealthyCheck", _ => Task.FromResult(HealthCheckResult.Failed(UnhealthyMessage, exception))); + b.AddAsyncCheck("HealthyCheck", _ => Task.FromResult(HealthCheckResult.Healthy(HealthyMessage, data))); + b.AddAsyncCheck("DegradedCheck", _ => Task.FromResult(HealthCheckResult.Degraded(DegradedMessage))); + b.AddAsyncCheck("UnhealthyCheck", _ => Task.FromResult(HealthCheckResult.Unhealthy(UnhealthyMessage, exception))); }); // Act @@ -201,7 +203,7 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks insideCheck.SetResult(null); await Task.Delay(10000, ct); - return HealthCheckResult.Failed(); + return HealthCheckResult.Unhealthy(); }); }); @@ -218,7 +220,7 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks } [Fact] - public async Task CheckHealthAsync_ConvertsExceptionInHealthCheckToFailedResultAsync() + public async Task CheckHealthAsync_ConvertsExceptionInHealthCheckToUnhealthyResultAsync() { // Arrange var thrownException = new InvalidOperationException("Whoops!"); @@ -228,7 +230,7 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks { b.AddAsyncCheck("Throws", ct => throw thrownException); b.AddAsyncCheck("Faults", ct => Task.FromException(faultedException)); - b.AddAsyncCheck("Succeeds", ct => Task.FromResult(HealthCheckResult.Passed())); + b.AddAsyncCheck("Succeeds", ct => Task.FromResult(HealthCheckResult.Healthy())); }); // Act @@ -241,14 +243,14 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks { Assert.Equal("Throws", actual.Key); Assert.Equal(thrownException.Message, actual.Value.Description); - Assert.Equal(HealthStatus.Failed, actual.Value.Status); + Assert.Equal(HealthStatus.Unhealthy, actual.Value.Status); Assert.Same(thrownException, actual.Value.Exception); }, actual => { Assert.Equal("Faults", actual.Key); Assert.Equal(faultedException.Message, actual.Value.Description); - Assert.Equal(HealthStatus.Failed, actual.Value.Status); + Assert.Equal(HealthStatus.Unhealthy, actual.Value.Status); Assert.Same(faultedException, actual.Value.Exception); }, actual => @@ -278,7 +280,7 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks Assert.Equal("TestScope", item.Value); }); }); - return Task.FromResult(HealthCheckResult.Passed()); + return Task.FromResult(HealthCheckResult.Healthy()); }); var loggerFactory = new TestLoggerFactory(sink, enabled: true); @@ -398,7 +400,7 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { - return Task.FromResult(HealthCheckResult.Passed()); + return Task.FromResult(HealthCheckResult.Healthy()); } } @@ -410,7 +412,7 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks { { "name", context.Registration.Name }, }; - return Task.FromResult(HealthCheckResult.Passed(data: data)); + return Task.FromResult(HealthCheckResult.Healthy(data: data)); } } } diff --git a/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/DependencyInjection/HealthChecksBuilderTest.cs b/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/DependencyInjection/HealthChecksBuilderTest.cs index c4974013c7..4235f152a2 100644 --- a/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/DependencyInjection/HealthChecksBuilderTest.cs +++ b/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/DependencyInjection/HealthChecksBuilderTest.cs @@ -20,7 +20,7 @@ namespace Microsoft.Extensions.DependencyInjection // Arrange var instance = new DelegateHealthCheck((_) => { - return Task.FromResult(HealthCheckResult.Passed()); + return Task.FromResult(HealthCheckResult.Healthy()); }); var services = CreateServices(); @@ -112,9 +112,9 @@ namespace Microsoft.Extensions.DependencyInjection { // Arrange var services = CreateServices(); - services.AddHealthChecks().AddCheck("test", failureStatus: HealthStatus.Degraded, tags: new[] { "tag", }, check: () => + services.AddHealthChecks().AddCheck("test", tags: new[] { "tag", }, check: () => { - return HealthCheckResult.Passed(); + return HealthCheckResult.Healthy(); }); var serviceProvider = services.BuildServiceProvider(); @@ -125,7 +125,7 @@ namespace Microsoft.Extensions.DependencyInjection // Assert var registration = Assert.Single(options.Registrations); Assert.Equal("test", registration.Name); - Assert.Equal(HealthStatus.Degraded, registration.FailureStatus); + Assert.Equal(HealthStatus.Unhealthy, registration.FailureStatus); Assert.Equal(new[] { "tag", }, registration.Tags); Assert.IsType(registration.Factory(serviceProvider)); } @@ -137,8 +137,8 @@ namespace Microsoft.Extensions.DependencyInjection var services = CreateServices(); services.AddHealthChecks().AddCheck("test", (_) => { - return HealthCheckResult.Passed(); - }, failureStatus: HealthStatus.Degraded, tags: new[] { "tag", }); + return HealthCheckResult.Degraded(); + }, tags: new[] { "tag", }); var serviceProvider = services.BuildServiceProvider(); @@ -148,7 +148,7 @@ namespace Microsoft.Extensions.DependencyInjection // Assert var registration = Assert.Single(options.Registrations); Assert.Equal("test", registration.Name); - Assert.Equal(HealthStatus.Degraded, registration.FailureStatus); + Assert.Equal(HealthStatus.Unhealthy, registration.FailureStatus); Assert.Equal(new[] { "tag", }, registration.Tags); Assert.IsType(registration.Factory(serviceProvider)); } @@ -160,8 +160,8 @@ namespace Microsoft.Extensions.DependencyInjection var services = CreateServices(); services.AddHealthChecks().AddAsyncCheck("test", () => { - return Task.FromResult(HealthCheckResult.Passed()); - }, failureStatus: HealthStatus.Degraded, tags: new[] { "tag", }); + return Task.FromResult(HealthCheckResult.Healthy()); + }, tags: new[] { "tag", }); var serviceProvider = services.BuildServiceProvider(); @@ -171,7 +171,7 @@ namespace Microsoft.Extensions.DependencyInjection // Assert var registration = Assert.Single(options.Registrations); Assert.Equal("test", registration.Name); - Assert.Equal(HealthStatus.Degraded, registration.FailureStatus); + Assert.Equal(HealthStatus.Unhealthy, registration.FailureStatus); Assert.Equal(new[] { "tag", }, registration.Tags); Assert.IsType(registration.Factory(serviceProvider)); } @@ -183,8 +183,8 @@ namespace Microsoft.Extensions.DependencyInjection var services = CreateServices(); services.AddHealthChecks().AddAsyncCheck("test", (_) => { - return Task.FromResult(HealthCheckResult.Passed()); - }, failureStatus: HealthStatus.Degraded, tags: new[] { "tag", }); + return Task.FromResult(HealthCheckResult.Unhealthy()); + }, tags: new[] { "tag", }); var serviceProvider = services.BuildServiceProvider(); @@ -194,7 +194,7 @@ namespace Microsoft.Extensions.DependencyInjection // Assert var registration = Assert.Single(options.Registrations); Assert.Equal("test", registration.Name); - Assert.Equal(HealthStatus.Degraded, registration.FailureStatus); + Assert.Equal(HealthStatus.Unhealthy, registration.FailureStatus); Assert.Equal(new[] { "tag", }, registration.Tags); Assert.IsType(registration.Factory(serviceProvider)); } @@ -205,10 +205,10 @@ namespace Microsoft.Extensions.DependencyInjection var services = new ServiceCollection(); services .AddHealthChecks() - .AddAsyncCheck("Foo", () => Task.FromResult(HealthCheckResult.Passed())); + .AddAsyncCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy())); services .AddHealthChecks() - .AddAsyncCheck("Bar", () => Task.FromResult(HealthCheckResult.Passed())); + .AddAsyncCheck("Bar", () => Task.FromResult(HealthCheckResult.Healthy())); // Act var options = services.BuildServiceProvider().GetRequiredService>(); diff --git a/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/HealthCheckPublisherHostedServiceTest.cs b/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/HealthCheckPublisherHostedServiceTest.cs index 49d57916eb..94687efcb8 100644 --- a/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/HealthCheckPublisherHostedServiceTest.cs +++ b/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/HealthCheckPublisherHostedServiceTest.cs @@ -448,8 +448,8 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks serviceCollection.AddOptions(); serviceCollection.AddLogging(); serviceCollection.AddHealthChecks() - .AddCheck("one", () => { return HealthCheckResult.Passed(); }) - .AddCheck("two", () => { return HealthCheckResult.Passed(); }); + .AddCheck("one", () => { return HealthCheckResult.Healthy(); }) + .AddCheck("two", () => { return HealthCheckResult.Healthy(); }); // Choosing big values for tests to make sure that we're not dependent on the defaults. // All of the tests that rely on the timer will set their own values for speed. diff --git a/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/HealthReportTest.cs b/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/HealthReportTest.cs index 453398e639..07f8e5a8e3 100644 --- a/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/HealthReportTest.cs +++ b/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/HealthReportTest.cs @@ -13,7 +13,6 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks [InlineData(HealthStatus.Healthy)] [InlineData(HealthStatus.Degraded)] [InlineData(HealthStatus.Unhealthy)] - [InlineData(HealthStatus.Failed)] public void Status_MatchesWorstStatusInResults(HealthStatus status) { var result = new HealthReport(new Dictionary()