From a620ce2850f7ef2772644641b06724394d828245 Mon Sep 17 00:00:00 2001 From: Stu Frankish Date: Mon, 13 May 2024 23:48:14 +0100 Subject: [PATCH] Implemented configuration options --- HealthChecks.UnitTests/TestSuite.cs | 37 +++++++++++++- HealthChecks.Uptime/Constants.cs | 1 + .../Extensions/ServiceCollectionExtension.cs | 22 ++++++++- .../HealthCheck/StartupTimeHealthCheck.cs | 49 +++++++++++++++++-- .../HealthChecks.Uptime.csproj | 2 +- .../Options/UptimeHealthCheckOptions.cs | 6 +++ 6 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 HealthChecks.Uptime/Options/UptimeHealthCheckOptions.cs diff --git a/HealthChecks.UnitTests/TestSuite.cs b/HealthChecks.UnitTests/TestSuite.cs index 3520194..d7910f1 100644 --- a/HealthChecks.UnitTests/TestSuite.cs +++ b/HealthChecks.UnitTests/TestSuite.cs @@ -1,4 +1,5 @@ using HealthChecks.Uptime; +using HealthChecks.Uptime.Options; using Microsoft.Extensions.Diagnostics.HealthChecks; namespace HealthChecks.UnitTests; @@ -10,13 +11,14 @@ public class TestSuite { private readonly DateTime _fixedStartupTime = new(2023, 1, 1); private readonly StartupTimeHealthCheck _healthCheckWithFixedTime; + private readonly UptimeHealthCheckOptions _healthCheckOptions = new(); /// /// Initializes a new instance of the class. /// public TestSuite() { - _healthCheckWithFixedTime = new StartupTimeHealthCheck(_fixedStartupTime); + _healthCheckWithFixedTime = new StartupTimeHealthCheck(_fixedStartupTime, _healthCheckOptions); } /// @@ -30,7 +32,7 @@ public TestSuite() public async Task CheckHealthAsync_Should_ReturnHealthy(int hoursOffset) { // Arrange - var healthCheck = hoursOffset == 0 ? _healthCheckWithFixedTime : new StartupTimeHealthCheck(DateTime.Now.AddHours(hoursOffset)); + var healthCheck = hoursOffset == 0 ? _healthCheckWithFixedTime : new StartupTimeHealthCheck(DateTime.Now.AddHours(hoursOffset), _healthCheckOptions); // Act var result = await healthCheck.CheckHealthAsync(new HealthCheckContext()); @@ -57,4 +59,35 @@ public async Task CheckHealthAsync_Should_ReturnHealthy_WithCustomStartupTime() Assert.True(data.ContainsKey("Uptime")); Assert.Equal(_fixedStartupTime.ToString("o"), data["Startup Time"].ToString()); } + /// + /// Tests the CheckHealthAsync method to ensure it returns a degraded status when DegradedThresholdInSeconds is set. + /// + /// The number of hours offset from the startup time. + /// A task representing the asynchronous operation. + [Theory] + [InlineData(0)] // At startup + [InlineData(-1)] // After 1 hour of uptime + public async Task CheckHealthAsync_Should_ReturnDegraded_WhenDegradedThresholdInSecondsIsSet(int hoursOffset) + { + // Arrange + var degradedThresholdInSeconds = 3600; // Set the degraded threshold to 1 hour + _healthCheckOptions.DegradedThresholdInSeconds = degradedThresholdInSeconds; + + var healthCheck = new StartupTimeHealthCheck(DateTime.Now.AddHours(hoursOffset), _healthCheckOptions); + + // Act + var result = await healthCheck.CheckHealthAsync(new HealthCheckContext()); + + // Assert + if (hoursOffset == 0) + { + Assert.Equal(HealthStatus.Degraded, result.Status); + Assert.Contains("Application is experiencing degraded service.", result.Description); + } + else + { + Assert.Equal(HealthStatus.Healthy, result.Status); + Assert.Contains("Application has been running without issues.", result.Description); + } + } } diff --git a/HealthChecks.Uptime/Constants.cs b/HealthChecks.Uptime/Constants.cs index fa005d8..580ce98 100644 --- a/HealthChecks.Uptime/Constants.cs +++ b/HealthChecks.Uptime/Constants.cs @@ -12,5 +12,6 @@ internal sealed class Language public const string Data_Uptime = "Uptime"; public const string Data_StartupTime = "Startup Time"; public const string Data_HealthyDescription = "Application has been running without issues."; + public const string Data_DegradedDescription = "Application is experiencing degraded service."; } } diff --git a/HealthChecks.Uptime/Extensions/ServiceCollectionExtension.cs b/HealthChecks.Uptime/Extensions/ServiceCollectionExtension.cs index 6f75134..fc8ae4b 100644 --- a/HealthChecks.Uptime/Extensions/ServiceCollectionExtension.cs +++ b/HealthChecks.Uptime/Extensions/ServiceCollectionExtension.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using HealthChecks.Uptime.Options; +using Microsoft.Extensions.DependencyInjection; namespace HealthChecks.Uptime; @@ -11,7 +12,24 @@ public static class ServiceCollectionExtension /// The with the Uptime health check added. public static IHealthChecksBuilder AddUptimeHealthCheck(this IHealthChecksBuilder builder) { - builder.Services.AddSingleton(implementationInstance: new StartupTimeHealthCheck(DateTime.Now)); + builder.Services.AddSingleton(implementationInstance: new StartupTimeHealthCheck(DateTime.Now, options: null)); + builder.AddCheck(name: Constants.Configuration.DefaultCheckName); + + return builder; + } + + /// + /// Adds the Uptime health check to the with the specified options. + /// + /// The to add the health check to. + /// The options for the Uptime health check. + /// The with the Uptime health check added. + public static IHealthChecksBuilder AddUptimeHealthCheck(this IHealthChecksBuilder builder, Action? options) + { + UptimeHealthCheckOptions healthCheckOptions = new(); + options?.Invoke(healthCheckOptions); + + builder.Services.AddSingleton(implementationInstance: new StartupTimeHealthCheck(DateTime.Now, options: healthCheckOptions)); builder.AddCheck(name: Constants.Configuration.DefaultCheckName); return builder; diff --git a/HealthChecks.Uptime/HealthCheck/StartupTimeHealthCheck.cs b/HealthChecks.Uptime/HealthCheck/StartupTimeHealthCheck.cs index d100c19..3628d77 100644 --- a/HealthChecks.Uptime/HealthCheck/StartupTimeHealthCheck.cs +++ b/HealthChecks.Uptime/HealthCheck/StartupTimeHealthCheck.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Diagnostics.HealthChecks; +using HealthChecks.Uptime.Options; +using Microsoft.Extensions.Diagnostics.HealthChecks; namespace HealthChecks.Uptime; @@ -6,9 +7,10 @@ namespace HealthChecks.Uptime; /// Initializes a new instance of the class. /// /// The startup time of the application. -public sealed class StartupTimeHealthCheck(DateTime startupTime) : IHealthCheck +public sealed class StartupTimeHealthCheck(DateTime startupTime, UptimeHealthCheckOptions? options) : IHealthCheck { private readonly DateTime _startupTime = startupTime; + private readonly UptimeHealthCheckOptions _options = options ?? new(); /// /// Checks the health of the application's startup time. @@ -21,10 +23,51 @@ public Task CheckHealthAsync(HealthCheckContext context, Canc var uptime = DateTime.Now - _startupTime; var data = new Dictionary { + // Formats the startup time as a string using the "o" format specifier (ISO 8601 format) { Constants.Language.Data_StartupTime, _startupTime.ToString(format: "o") }, + + // Include the uptime as a string { Constants.Language.Data_Uptime, uptime.ToString() } }; - return Task.FromResult(HealthCheckResult.Healthy(description: Constants.Language.Data_HealthyDescription, data)); + // Shortcut for when the degraded threshold is not set or is less than 0 + if ( + _options.DegradedThresholdInSeconds is null || + _options.DegradedThresholdInSeconds.HasValue && _options.DegradedThresholdInSeconds.Value < 0 + ) + { + // Create a new HealthCheckResult object with the specified parameters + var shortResult = new HealthCheckResult( + status: HealthStatus.Healthy, + description: Constants.Language.Data_HealthyDescription, + data: data + ); + + return Task.FromResult(shortResult); + } + + // Normal operation + HealthStatus status; + string description; + + if (uptime.TotalSeconds < _options.DegradedThresholdInSeconds) + { + status = HealthStatus.Degraded; + description = Constants.Language.Data_DegradedDescription; + } + else + { + status = HealthStatus.Healthy; + description = Constants.Language.Data_HealthyDescription; + } + + // Create a new HealthCheckResult object with the specified parameters + var result = new HealthCheckResult( + status: status, + description: description, + data: data + ); + + return Task.FromResult(result); } } diff --git a/HealthChecks.Uptime/HealthChecks.Uptime.csproj b/HealthChecks.Uptime/HealthChecks.Uptime.csproj index 41dd265..ab2d014 100644 --- a/HealthChecks.Uptime/HealthChecks.Uptime.csproj +++ b/HealthChecks.Uptime/HealthChecks.Uptime.csproj @@ -8,7 +8,7 @@ Library True Uptime HealthCheck - 2.0.4 + 2.0.5 Stu Frankish Stu Frankish Application uptime Healthcheck extension. diff --git a/HealthChecks.Uptime/Options/UptimeHealthCheckOptions.cs b/HealthChecks.Uptime/Options/UptimeHealthCheckOptions.cs new file mode 100644 index 0000000..854855b --- /dev/null +++ b/HealthChecks.Uptime/Options/UptimeHealthCheckOptions.cs @@ -0,0 +1,6 @@ +namespace HealthChecks.Uptime.Options; + +public sealed class UptimeHealthCheckOptions +{ + public int? DegradedThresholdInSeconds { get; set; } +}