├── src ├── HealthCheck.Nancy │ ├── packages.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── HealthCheck.Nancy.nuspec │ ├── HealthCheckModule.cs │ └── HealthCheck.Nancy.csproj ├── HealthCheck.Redis │ ├── packages.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Metrics │ │ ├── RedisVersion.cs │ │ ├── RedisMemoryUsage.cs │ │ ├── RedisUptime.cs │ │ └── RedisMaxMemory.cs │ ├── HealthCheck.Redis.nuspec │ ├── Checkers │ │ ├── RedisIsAvailable.cs │ │ └── RedisHasFreeMemory.cs │ └── HealthCheck.Redis.csproj ├── HealthCheck.Core │ ├── IMetric.cs │ ├── IChecker.cs │ ├── CheckResult.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Helpers │ │ └── TaskExtensions.cs │ ├── HealthCheckResult.cs │ ├── HealthCheck.Core.nuspec │ ├── CheckerBase.cs │ ├── HealthCheck.Core.csproj │ └── HealthCheck.cs ├── Tests │ ├── HealthCheck.Core.Tests │ │ ├── packages.config │ │ ├── Properties │ │ │ └── AssemblyInfo.cs │ │ ├── CheckerBaseTests.cs │ │ ├── HealthCheck.Core.Tests.csproj │ │ └── HealthCheckTests.cs │ ├── HealthCheck.Redis.Tests │ │ ├── packages.config │ │ ├── Properties │ │ │ └── AssemblyInfo.cs │ │ ├── Checkers │ │ │ ├── RedisIsAvailableTests.cs │ │ │ └── RedisHasFreeMemoryTests.cs │ │ └── HealthCheck.Redis.Tests.csproj │ ├── HealthCheck.Windows.Tests │ │ ├── packages.config │ │ ├── Properties │ │ │ └── AssemblyInfo.cs │ │ ├── Checkers │ │ │ ├── CpuUsageIsOkTests.cs │ │ │ └── SystemDriveHasFreeSpaceTests.cs │ │ └── HealthCheck.Windows.Tests.csproj │ └── HealthCheck.Nancy.Tests │ │ ├── packages.config │ │ ├── NancyTestingExtensions.cs │ │ ├── Properties │ │ └── AssemblyInfo.cs │ │ ├── HealthCheck.Nancy.Tests.csproj │ │ └── HealthCheckModuleTests.cs ├── SharedAssemblyInfo.cs ├── HealthCheck.Windows │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Metrics │ │ ├── AvailableSystemDriveSpace.cs │ │ └── CpuUsage.cs │ ├── HealthCheck.Windows.nuspec │ ├── Checkers │ │ ├── CpuUsageIsOk.cs │ │ └── SystemDriveHasFreeSpace.cs │ └── HealthCheck.Windows.csproj └── HealthCheck.sln ├── LICENSE ├── .gitattributes ├── .gitignore └── README.md /src/HealthCheck.Nancy/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/HealthCheck.Redis/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/HealthCheck.Core/IMetric.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace HealthCheck.Core 4 | { 5 | public interface IMetric 6 | { 7 | Task Read(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/HealthCheck.Core/IChecker.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace HealthCheck.Core 4 | { 5 | public interface IChecker 6 | { 7 | string Name { get; } 8 | Task Check(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Core.Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Redis.Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Windows.Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/HealthCheck.Core/CheckResult.cs: -------------------------------------------------------------------------------- 1 | namespace HealthCheck.Core 2 | { 3 | public class CheckResult 4 | { 5 | public string Checker { get; set; } 6 | public bool Passed { get; set; } 7 | public string Output { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/SharedAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | [assembly: AssemblyCompany("Anders Fjeldstad")] 4 | [assembly: AssemblyProduct("Hihaj.HealthCheck")] 5 | [assembly: AssemblyCopyright("Copyright © Anders Fjeldstad 2015")] 6 | [assembly: AssemblyTrademark("")] 7 | [assembly: AssemblyVersion("0.0.0.0")] 8 | [assembly: AssemblyFileVersion("0.0.0.0")] -------------------------------------------------------------------------------- /src/HealthCheck.Core/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("HealthCheck.Core")] 5 | [assembly: AssemblyDescription("")] 6 | [assembly: AssemblyConfiguration("")] 7 | [assembly: AssemblyCulture("")] 8 | [assembly: ComVisible(false)] 9 | [assembly: Guid("e8fdafbe-1b56-413b-ab6e-10d3b086fa29")] 10 | -------------------------------------------------------------------------------- /src/HealthCheck.Nancy/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("HealthCheck.Nancy")] 5 | [assembly: AssemblyDescription("")] 6 | [assembly: AssemblyConfiguration("")] 7 | [assembly: AssemblyCulture("")] 8 | [assembly: ComVisible(false)] 9 | [assembly: Guid("e5b8d5ed-f312-498f-b963-1b0760fa80df")] 10 | -------------------------------------------------------------------------------- /src/HealthCheck.Redis/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("HealthCheck.Redis")] 5 | [assembly: AssemblyDescription("")] 6 | [assembly: AssemblyConfiguration("")] 7 | [assembly: AssemblyCulture("")] 8 | [assembly: ComVisible(false)] 9 | [assembly: Guid("fe7f9cdb-9f43-4984-9ed7-3ff1f1c810f0")] 10 | -------------------------------------------------------------------------------- /src/HealthCheck.Windows/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("HealthCheck.Windows")] 5 | [assembly: AssemblyDescription("")] 6 | [assembly: AssemblyConfiguration("")] 7 | [assembly: AssemblyCulture("")] 8 | [assembly: ComVisible(false)] 9 | [assembly: Guid("c451395c-20f9-4d8f-af98-9953050c2204")] 10 | -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Nancy.Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Nancy.Tests/NancyTestingExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Nancy.Testing; 7 | using Newtonsoft.Json.Linq; 8 | 9 | namespace HealthCheck.Nancy.Tests 10 | { 11 | public static class NancyTestingExtensions 12 | { 13 | public static dynamic AsJson(this BrowserResponseBodyWrapper bodyWrapper) 14 | { 15 | return JObject.Parse(bodyWrapper.AsString()); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/HealthCheck.Core/Helpers/TaskExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace HealthCheck.Core.Helpers 5 | { 6 | public static class TaskExtensions 7 | { 8 | public static async Task TimeoutAfter(this Task task, TimeSpan timeout) 9 | { 10 | if (task == await Task.WhenAny(task, Task.Delay(timeout)).ConfigureAwait(false)) 11 | { 12 | return await task.ConfigureAwait(false); 13 | } 14 | throw new TimeoutException(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/HealthCheck.Core/HealthCheckResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace HealthCheck.Core 5 | { 6 | public class HealthCheckResult 7 | { 8 | private readonly CheckResult[] _results; 9 | private readonly bool _passed; 10 | 11 | public CheckResult[] Results { get { return _results; } } 12 | public bool Passed { get { return _passed; } } 13 | public string Status { get { return _passed ? "success" : "failure"; } } 14 | 15 | public HealthCheckResult(IEnumerable results) 16 | { 17 | _results = results.OrderBy(x => x.Checker).ToArray(); 18 | _passed = _results.All(x => x.Passed); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/HealthCheck.Core/HealthCheck.Core.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hihaj.HealthCheck 5 | $version$ 6 | Anders Fjeldstad 7 | Anders Fjeldstad 8 | https://raw.githubusercontent.com/Hihaj/HealthCheck/master/LICENSE 9 | https://github.com/Hihaj/HealthCheck 10 | false 11 | Flexible asynchronous healthcheck for apps. 12 | 13 | Anders Fjeldstad 2015 14 | async healthcheck 15 | 16 | -------------------------------------------------------------------------------- /src/HealthCheck.Redis/Metrics/RedisVersion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using HealthCheck.Core; 5 | using StackExchange.Redis; 6 | 7 | namespace HealthCheck.Redis.Metrics 8 | { 9 | public interface IRedisVersion : IMetric 10 | { 11 | } 12 | 13 | public class RedisVersion : IRedisVersion 14 | { 15 | private readonly Func _redisServer; 16 | 17 | public RedisVersion(Func redisServer) 18 | { 19 | _redisServer = redisServer; 20 | } 21 | 22 | public async Task Read() 23 | { 24 | var info = (await _redisServer().InfoAsync("Server").ConfigureAwait(false)).First(); 25 | return info.First(x => x.Key.Equals("redis_version")).Value; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/HealthCheck.Redis/Metrics/RedisMemoryUsage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using HealthCheck.Core; 5 | using StackExchange.Redis; 6 | 7 | namespace HealthCheck.Redis.Metrics 8 | { 9 | public interface IRedisMemoryUsage : IMetric 10 | { 11 | } 12 | 13 | public class RedisMemoryUsage : IRedisMemoryUsage 14 | { 15 | private readonly Func _redisServer; 16 | 17 | public RedisMemoryUsage(Func redisServer) 18 | { 19 | _redisServer = redisServer; 20 | } 21 | 22 | public async Task Read() 23 | { 24 | var info = (await _redisServer().InfoAsync("Memory").ConfigureAwait(false)).First(); 25 | return long.Parse(info.First(x => x.Key.Equals("used_memory")).Value); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/HealthCheck.Windows/Metrics/AvailableSystemDriveSpace.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using HealthCheck.Core; 6 | 7 | namespace HealthCheck.Windows.Metrics 8 | { 9 | public interface IAvailableSystemDriveSpace : IMetric 10 | { 11 | } 12 | 13 | public class AvailableSystemDriveSpace : IAvailableSystemDriveSpace 14 | { 15 | public Task Read() 16 | { 17 | var systemDriveName = Path.GetPathRoot(Environment.GetFolderPath(Environment.SpecialFolder.System)); 18 | return Task.FromResult( 19 | DriveInfo.GetDrives() 20 | .Single(x => x.IsReady && x.Name.Equals(systemDriveName, StringComparison.OrdinalIgnoreCase)) 21 | .AvailableFreeSpace); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/HealthCheck.Redis/Metrics/RedisUptime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using HealthCheck.Core; 5 | using StackExchange.Redis; 6 | 7 | namespace HealthCheck.Redis.Metrics 8 | { 9 | public interface IRedisUptime : IMetric 10 | { 11 | } 12 | 13 | public class RedisUptime : IRedisUptime 14 | { 15 | private readonly Func _redisServer; 16 | 17 | public RedisUptime(Func redisServer) 18 | { 19 | _redisServer = redisServer; 20 | } 21 | 22 | public async Task Read() 23 | { 24 | var info = (await _redisServer().InfoAsync("Server").ConfigureAwait(false)).First(); 25 | return TimeSpan.FromSeconds(int.Parse(info.First(x => x.Key.Equals("uptime_in_seconds")).Value)); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/HealthCheck.Windows/HealthCheck.Windows.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hihaj.HealthCheck.Windows 5 | $version$ 6 | Anders Fjeldstad 7 | Anders Fjeldstad 8 | https://raw.githubusercontent.com/Hihaj/HealthCheck/master/LICENSE 9 | https://github.com/Hihaj/HealthCheck 10 | false 11 | Windows OS-specific checkers and metrics for Hihaj.HealthCheck. 12 | 13 | Anders Fjeldstad 2015 14 | async windows os healthcheck 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/HealthCheck.Redis/HealthCheck.Redis.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hihaj.HealthCheck.Redis 5 | $version$ 6 | Anders Fjeldstad 7 | Anders Fjeldstad 8 | https://raw.githubusercontent.com/Hihaj/HealthCheck/master/LICENSE 9 | https://github.com/Hihaj/HealthCheck 10 | false 11 | Redis checkers and metrics for Hihaj.HealthCheck. 12 | 13 | Anders Fjeldstad 2015 14 | redis async healthcheck 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/HealthCheck.Nancy/HealthCheck.Nancy.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hihaj.HealthCheck.Nancy 5 | $version$ 6 | Anders Fjeldstad 7 | Anders Fjeldstad 8 | https://raw.githubusercontent.com/Hihaj/HealthCheck/master/LICENSE 9 | https://github.com/Hihaj/HealthCheck 10 | false 11 | Nancy module for exposing a health check endpoint backed by Hihaj.HealthCheck. 12 | 13 | Anders Fjeldstad 2015 14 | nancy async healthcheck 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Anders Fjeldstad 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /src/HealthCheck.Redis/Metrics/RedisMaxMemory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using HealthCheck.Core; 7 | using StackExchange.Redis; 8 | 9 | namespace HealthCheck.Redis.Metrics 10 | { 11 | public interface IRedisMaxMemory : IMetric 12 | { 13 | } 14 | 15 | public class RedisMaxMemory : IRedisMaxMemory 16 | { 17 | private readonly Func _redisServer; 18 | 19 | public RedisMaxMemory(Func redisServer) 20 | { 21 | _redisServer = redisServer; 22 | } 23 | 24 | public async Task Read() 25 | { 26 | string maxMemory = (await _redisServer().ConfigGetAsync("maxmemory").ConfigureAwait(false)) 27 | .Select(x => x.Value) 28 | .FirstOrDefault(); 29 | if (string.IsNullOrEmpty(maxMemory)) 30 | { 31 | return null; 32 | } 33 | long maxMemoryBytes; 34 | return long.TryParse(maxMemory, out maxMemoryBytes) ? 35 | maxMemoryBytes : 36 | (long?)null; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/HealthCheck.Redis/Checkers/RedisIsAvailable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using HealthCheck.Core; 4 | using HealthCheck.Redis.Metrics; 5 | 6 | namespace HealthCheck.Redis.Checkers 7 | { 8 | public class RedisIsAvailable : CheckerBase 9 | { 10 | private readonly IRedisVersion _redisVersion; 11 | private readonly IRedisUptime _redisUptime; 12 | 13 | public override string Name 14 | { 15 | get { return "Redis is available"; } 16 | } 17 | 18 | public RedisIsAvailable( 19 | IRedisVersion redisVersion, 20 | IRedisUptime redisUptime) 21 | { 22 | _redisVersion = redisVersion; 23 | _redisUptime = redisUptime; 24 | } 25 | 26 | protected override async Task CheckCore() 27 | { 28 | var redisVersion = await _redisVersion.Read().ConfigureAwait(false); 29 | var redisUptime = await _redisUptime.Read().ConfigureAwait(false); 30 | return CreateResult( 31 | true, 32 | string.Format("Version {0}, uptime {1:%d} days {1:%h} hours {1:%m} minutes.", redisVersion, redisUptime)); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/HealthCheck.Core/CheckerBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using HealthCheck.Core.Helpers; 4 | 5 | namespace HealthCheck.Core 6 | { 7 | public abstract class CheckerBase : IChecker 8 | { 9 | private TimeSpan? _timeout = TimeSpan.FromSeconds(5); 10 | 11 | public abstract string Name { get; } 12 | public TimeSpan? Timeout { get { return _timeout; } set { _timeout = value; } } 13 | 14 | protected CheckResult CreateResult(bool passed, string output) 15 | { 16 | return new CheckResult 17 | { 18 | Checker = Name, 19 | Passed = passed, 20 | Output = output 21 | }; 22 | } 23 | 24 | public async Task Check() 25 | { 26 | try 27 | { 28 | if (Timeout.HasValue && Timeout.Value > TimeSpan.Zero) 29 | { 30 | return await CheckCore().TimeoutAfter(Timeout.Value).ConfigureAwait(false); 31 | } 32 | return await CheckCore().ConfigureAwait(false); 33 | } 34 | catch (Exception ex) 35 | { 36 | return CreateResult(false, ex.Message); 37 | } 38 | } 39 | 40 | protected abstract Task CheckCore(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/HealthCheck.Windows/Checkers/CpuUsageIsOk.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using HealthCheck.Core; 4 | using HealthCheck.Windows.Metrics; 5 | 6 | namespace HealthCheck.Windows.Checkers 7 | { 8 | public class CpuUsageIsOk : CheckerBase 9 | { 10 | private readonly ICpuUsage _cpuUsage; 11 | private readonly Options _options; 12 | 13 | public override string Name 14 | { 15 | get { return "CPU usage is OK"; } 16 | } 17 | 18 | public CpuUsageIsOk( 19 | ICpuUsage cpuUsage, 20 | Options options = null) 21 | { 22 | _cpuUsage = cpuUsage; 23 | _options = options ?? new Options(); 24 | } 25 | 26 | protected override async Task CheckCore() 27 | { 28 | var currentCpuUsage = await _cpuUsage.Read().ConfigureAwait(false); 29 | return CreateResult( 30 | currentCpuUsage < _options.CpuUsageWarningThresholdInPercent, 31 | string.Format("Currently using {0:F1} % of total processor time.", currentCpuUsage)); 32 | } 33 | 34 | public class Options 35 | { 36 | public float CpuUsageWarningThresholdInPercent { get; set; } 37 | 38 | public Options() 39 | { 40 | CpuUsageWarningThresholdInPercent = 90; 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/HealthCheck.Windows/Metrics/CpuUsage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Threading.Tasks; 4 | using System.Timers; 5 | using HealthCheck.Core; 6 | 7 | namespace HealthCheck.Windows.Metrics 8 | { 9 | public interface ICpuUsage : IMetric 10 | { 11 | } 12 | 13 | public class CpuUsage : ICpuUsage, IDisposable 14 | { 15 | private static readonly object Token = new object(); 16 | private readonly PerformanceCounter _performanceCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total"); 17 | private readonly Timer _timer = new Timer(); 18 | private float _lastCpuUsageValue; 19 | 20 | public CpuUsage() 21 | { 22 | _performanceCounter.NextValue(); 23 | _timer.AutoReset = true; 24 | _timer.Interval = TimeSpan.FromSeconds(10).TotalMilliseconds; 25 | _timer.Elapsed += (sender, args) => 26 | { 27 | lock (Token) 28 | { 29 | _lastCpuUsageValue = _performanceCounter.NextValue(); 30 | } 31 | }; 32 | _timer.Start(); 33 | } 34 | 35 | public Task Read() 36 | { 37 | lock (Token) 38 | { 39 | return Task.FromResult(_lastCpuUsageValue); 40 | } 41 | } 42 | 43 | public void Dispose() 44 | { 45 | _timer.Stop(); 46 | _timer.Dispose(); 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Core.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("HealthCheck.Core.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("HealthCheck.Core.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("4ab25e02-146b-44ce-bccc-fa3ca8a099b2")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Nancy.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("HealthCheck.Nancy.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("HealthCheck.Nancy.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("635cc6c2-5bab-4c40-bd6c-534bf0cc057d")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Redis.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("HealthCheck.Redis.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("HealthCheck.Redis.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("5ab28921-7785-4ffc-ad6d-dce7c004b85f")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Windows.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("HealthCheck.Windows.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("HealthCheck.Windows.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("823b7ef3-082b-4d22-a0b6-5470400e23b1")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/HealthCheck.Windows/Checkers/SystemDriveHasFreeSpace.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using HealthCheck.Core; 4 | using HealthCheck.Windows.Metrics; 5 | 6 | namespace HealthCheck.Windows.Checkers 7 | { 8 | public class SystemDriveHasFreeSpace : CheckerBase 9 | { 10 | private readonly IAvailableSystemDriveSpace _availableSystemDriveSpace; 11 | private readonly Options _options; 12 | 13 | public override string Name 14 | { 15 | get { return "System drive has free space"; } 16 | } 17 | 18 | public SystemDriveHasFreeSpace(IAvailableSystemDriveSpace availableSystemDriveSpace, Options options = null) 19 | { 20 | _availableSystemDriveSpace = availableSystemDriveSpace; 21 | _options = options ?? new Options(); 22 | } 23 | 24 | protected override async Task CheckCore() 25 | { 26 | var freeBytes = await _availableSystemDriveSpace.Read().ConfigureAwait(false); 27 | return CreateResult( 28 | freeBytes >= _options.SystemDriveAvailableFreeSpaceWarningThresholdInBytes, 29 | string.Format("{0:F1} GB available.", freeBytes / (double)(1024 * 1024 * 1024))); 30 | } 31 | 32 | public class Options 33 | { 34 | public long SystemDriveAvailableFreeSpaceWarningThresholdInBytes { get; set; } 35 | 36 | public Options() 37 | { 38 | SystemDriveAvailableFreeSpaceWarningThresholdInBytes = 5L * 1024L * 1024L * 1024L; // 5 GB 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Core.Tests/CheckerBaseTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | 8 | namespace HealthCheck.Core.Tests 9 | { 10 | [TestFixture] 11 | public class CheckerBaseTests 12 | { 13 | [Test] 14 | public async Task CheckFailsOnTimeout() 15 | { 16 | // Arrange 17 | var delay = TimeSpan.FromSeconds(3); 18 | var checker = new DummyChecker(async () => 19 | { 20 | await Task.Delay(delay); 21 | return new CheckResult { Passed = true }; 22 | }); 23 | checker.Timeout = TimeSpan.FromSeconds(delay.TotalSeconds / 2); 24 | 25 | // Act 26 | var result = await checker.Check(); 27 | 28 | // Assert 29 | Assert.That(result.Passed, Is.False); 30 | Assert.That(result.Output, Is.EqualTo(new TimeoutException().Message)); 31 | } 32 | 33 | public class DummyChecker : CheckerBase 34 | { 35 | private readonly Func> _check; 36 | 37 | public override string Name 38 | { 39 | get { return "Dummy checker"; } 40 | } 41 | 42 | public DummyChecker(Func> check) 43 | { 44 | _check = check; 45 | } 46 | 47 | protected override async Task CheckCore() 48 | { 49 | return await _check().ConfigureAwait(false); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/HealthCheck.Nancy/HealthCheckModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using HealthCheck.Core; 7 | using Nancy; 8 | using Nancy.Security; 9 | 10 | namespace HealthCheck.Nancy 11 | { 12 | public class HealthCheckModule : NancyModule 13 | { 14 | public const string DefaultRoute = "/healthcheck"; 15 | 16 | public HealthCheckModule(IEnumerable checkers, HealthCheckOptions options = null) 17 | { 18 | if (options == null) 19 | { 20 | options = new HealthCheckOptions(); 21 | } 22 | if (options.RequireHttps) 23 | { 24 | this.RequiresHttps(redirect: false); 25 | } 26 | 27 | Get[options.Route, true] = async (_, ct) => 28 | { 29 | if (options.AuthorizationCallback != null && !await options.AuthorizationCallback(Context)) 30 | { 31 | return HttpStatusCode.Unauthorized; 32 | } 33 | return Response.AsJson(await (new Core.HealthCheck(checkers).Run())); 34 | }; 35 | } 36 | } 37 | 38 | public class HealthCheckOptions 39 | { 40 | public string Route { get; set; } 41 | public bool RequireHttps { get; set; } 42 | public Func> AuthorizationCallback { get; set; } 43 | 44 | public HealthCheckOptions() 45 | { 46 | Route = "/healthcheck"; 47 | RequireHttps = true; 48 | AuthorizationCallback = null; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Redis.Tests/Checkers/RedisIsAvailableTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using HealthCheck.Redis.Checkers; 7 | using HealthCheck.Redis.Metrics; 8 | using Moq; 9 | using NUnit.Framework; 10 | 11 | namespace HealthCheck.Redis.Tests.Checkers 12 | { 13 | [TestFixture] 14 | public class RedisIsAvailableTests 15 | { 16 | [Test] 17 | public async Task CheckFailsWhenRedisVersionThrows() 18 | { 19 | // Arrange 20 | var redisVersionMock = new Mock(); 21 | var exception = new Exception("error message"); 22 | redisVersionMock.Setup(x => x.Read()).ThrowsAsync(exception); 23 | var redisUptimeMock = new Mock(); 24 | redisUptimeMock.Setup(x => x.Read()).ReturnsAsync(TimeSpan.FromDays(1)); 25 | var check = new RedisIsAvailable(redisVersionMock.Object, redisUptimeMock.Object); 26 | 27 | // Act 28 | var result = await check.Check(); 29 | 30 | // Assert 31 | Assert.That(result.Passed, Is.False); 32 | Assert.That(result.Output, Is.EqualTo(exception.Message)); 33 | } 34 | 35 | [Test] 36 | public async Task CheckFailsWhenRedisUptimeThrows() 37 | { 38 | // Arrange 39 | var redisVersionMock = new Mock(); 40 | redisVersionMock.Setup(x => x.Read()).ReturnsAsync("x"); 41 | var redisUptimeMock = new Mock(); 42 | var exception = new Exception("error message"); 43 | redisUptimeMock.Setup(x => x.Read()).ThrowsAsync(exception); 44 | var check = new RedisIsAvailable(redisVersionMock.Object, redisUptimeMock.Object); 45 | 46 | // Act 47 | var result = await check.Check(); 48 | 49 | // Assert 50 | Assert.That(result.Passed, Is.False); 51 | Assert.That(result.Output, Is.EqualTo(exception.Message)); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Windows.Tests/Checkers/CpuUsageIsOkTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using HealthCheck.Windows.Checkers; 7 | using HealthCheck.Windows.Metrics; 8 | using Moq; 9 | using NUnit.Framework; 10 | 11 | namespace HealthCheck.Windows.Tests.Checkers 12 | { 13 | [TestFixture] 14 | public class CpuUsageIsOkTests 15 | { 16 | [Test] 17 | public async Task CheckPassesWhenCpuUsageIsBelowThreshold() 18 | { 19 | // Arrange 20 | var options = new CpuUsageIsOk.Options(); 21 | var cpuUsageMock = new Mock(); 22 | cpuUsageMock.Setup(x => x.Read()).ReturnsAsync(options.CpuUsageWarningThresholdInPercent - 1); 23 | var check = new CpuUsageIsOk(cpuUsageMock.Object); 24 | 25 | // Act 26 | var result = await check.Check(); 27 | 28 | // Assert 29 | Assert.That(result.Passed, Is.True); 30 | } 31 | 32 | [Test] 33 | public async Task CheckFailsWhenCpuUsageIsAboveThreshold() 34 | { 35 | // Arrange 36 | var options = new CpuUsageIsOk.Options(); 37 | var cpuUsageMock = new Mock(); 38 | cpuUsageMock.Setup(x => x.Read()).ReturnsAsync(options.CpuUsageWarningThresholdInPercent + 1); 39 | var check = new CpuUsageIsOk(cpuUsageMock.Object); 40 | 41 | // Act 42 | var result = await check.Check(); 43 | 44 | // Assert 45 | Assert.That(result.Passed, Is.False); 46 | } 47 | 48 | [Test] 49 | public async Task CheckFailsWhenCpuUsageThrows() 50 | { 51 | // Arrange 52 | var exception = new Exception("error message"); 53 | var cpuUsageMock = new Mock(); 54 | cpuUsageMock.Setup(x => x.Read()).ThrowsAsync(exception); 55 | var check = new CpuUsageIsOk(cpuUsageMock.Object); 56 | 57 | // Act 58 | var result = await check.Check(); 59 | 60 | // Assert 61 | Assert.That(result.Passed, Is.False); 62 | Assert.That(result.Output, Is.EqualTo(exception.Message)); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/HealthCheck.Redis/Checkers/RedisHasFreeMemory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using HealthCheck.Core; 4 | using HealthCheck.Redis.Metrics; 5 | 6 | namespace HealthCheck.Redis.Checkers 7 | { 8 | public class RedisHasFreeMemory : CheckerBase 9 | { 10 | private readonly IRedisMemoryUsage _redisMemoryUsage; 11 | private readonly IRedisMaxMemory _redisMaxMemory; 12 | private readonly Options _options; 13 | 14 | public override string Name 15 | { 16 | get { return "Redis has free memory"; } 17 | } 18 | 19 | public RedisHasFreeMemory( 20 | IRedisMemoryUsage redisMemoryUsage, 21 | IRedisMaxMemory redisMaxMemory, 22 | Options options = null) 23 | { 24 | _redisMemoryUsage = redisMemoryUsage; 25 | _redisMaxMemory = redisMaxMemory; 26 | _options = options ?? new Options(); 27 | } 28 | 29 | protected override async Task CheckCore() 30 | { 31 | var maxBytes = await _redisMaxMemory.Read().ConfigureAwait(false); 32 | var usedBytes = await _redisMemoryUsage.Read().ConfigureAwait(false); 33 | if (!maxBytes.HasValue || maxBytes.Value == 0) 34 | { 35 | return CreateResult( 36 | true, 37 | string.Format( 38 | "Currently using {0:F1} MB, no hard upper limit configured.", 39 | usedBytes / (double)(1024 * 1024))); 40 | } 41 | var usage = usedBytes / (double)maxBytes; 42 | var freeMegabytes = (maxBytes - usedBytes) / (double)(1024 * 1024); 43 | return CreateResult( 44 | maxBytes - usedBytes > _options.RedisFreeMemoryWarningThresholdInBytes, 45 | string.Format( 46 | "{0:P0} of the reserved memory is used ({1:F1} MB free).", 47 | usage, 48 | freeMegabytes)); 49 | } 50 | 51 | public class Options 52 | { 53 | public long RedisFreeMemoryWarningThresholdInBytes { get; set; } 54 | 55 | public Options() 56 | { 57 | RedisFreeMemoryWarningThresholdInBytes = 50L * 1024L * 1024L; // 50 MB 58 | } 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Windows.Tests/Checkers/SystemDriveHasFreeSpaceTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using HealthCheck.Windows.Checkers; 7 | using HealthCheck.Windows.Metrics; 8 | using Moq; 9 | using NUnit.Framework; 10 | 11 | namespace HealthCheck.Windows.Tests.Checkers 12 | { 13 | [TestFixture] 14 | public class SystemDriveHasFreeSpaceTests 15 | { 16 | [Test] 17 | public async Task CheckPassesWhenFreeSpaceIsAboveThreshold() 18 | { 19 | // Arrange 20 | var options = new SystemDriveHasFreeSpace.Options(); 21 | var driveSpaceMock = new Mock(); 22 | driveSpaceMock.Setup(x => x.Read()).ReturnsAsync(options.SystemDriveAvailableFreeSpaceWarningThresholdInBytes + 1); 23 | var check = new SystemDriveHasFreeSpace(driveSpaceMock.Object); 24 | 25 | // Act 26 | var result = await check.Check(); 27 | 28 | // Assert 29 | Assert.That(result.Passed, Is.True); 30 | } 31 | 32 | [Test] 33 | public async Task CheckFailsWhenFreeSpaceIsBelowThreshold() 34 | { 35 | // Arrange 36 | var options = new SystemDriveHasFreeSpace.Options(); 37 | var driveSpaceMock = new Mock(); 38 | driveSpaceMock.Setup(x => x.Read()).ReturnsAsync(options.SystemDriveAvailableFreeSpaceWarningThresholdInBytes - 1); 39 | var check = new SystemDriveHasFreeSpace(driveSpaceMock.Object); 40 | 41 | // Act 42 | var result = await check.Check(); 43 | 44 | // Assert 45 | Assert.That(result.Passed, Is.False); 46 | } 47 | 48 | [Test] 49 | public async Task CheckFailsWhenFreeSpaceThrows() 50 | { 51 | // Arrange 52 | var exception = new Exception("error message"); 53 | var driveSpaceMock = new Mock(); 54 | driveSpaceMock.Setup(x => x.Read()).ThrowsAsync(exception); 55 | var check = new SystemDriveHasFreeSpace(driveSpaceMock.Object); 56 | 57 | // Act 58 | var result = await check.Check(); 59 | 60 | // Assert 61 | Assert.That(result.Passed, Is.False); 62 | Assert.That(result.Output, Is.EqualTo(exception.Message)); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /src/HealthCheck.Core/HealthCheck.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {7052554B-0005-47E4-B3C3-318D41261E72} 8 | Library 9 | Properties 10 | HealthCheck.Core 11 | HealthCheck.Core 12 | v4.5.1 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | Properties\SharedAssemblyInfo.cs 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 58 | -------------------------------------------------------------------------------- /src/HealthCheck.Core/HealthCheck.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace HealthCheck.Core 7 | { 8 | public class HealthCheck 9 | { 10 | private readonly IEnumerable _checkers; 11 | 12 | public HealthCheck(IEnumerable checkers) 13 | { 14 | _checkers = checkers; 15 | } 16 | 17 | public async Task Run() 18 | { 19 | try 20 | { 21 | if (_checkers == null) 22 | { 23 | return new HealthCheckResult(Enumerable.Empty()); 24 | } 25 | // Run all checks in parallel (if possible). Catch exceptions for each 26 | // checker individually. 27 | var checkResults = _checkers 28 | .AsParallel() 29 | .Select(async x => 30 | { 31 | try 32 | { 33 | return await x.Check().ConfigureAwait(false); 34 | } 35 | catch (Exception ex) 36 | { 37 | return new CheckResult 38 | { 39 | Checker = x.Name, 40 | Passed = false, 41 | Output = ex.Message 42 | }; 43 | } 44 | }) 45 | .ToArray(); 46 | await Task.WhenAll(checkResults).ConfigureAwait(false); 47 | return new HealthCheckResult(checkResults.Select(x => x.Result)); 48 | } 49 | catch (AggregateException ex) 50 | { 51 | return new HealthCheckResult(ex.Flatten().InnerExceptions.Select(innerEx => new CheckResult 52 | { 53 | Checker = innerEx.TargetSite != null && !string.IsNullOrEmpty(innerEx.TargetSite.Name) ? 54 | innerEx.TargetSite.Name : 55 | "HealthCheck", 56 | Passed = false, 57 | Output = innerEx.Message 58 | })); 59 | } 60 | catch (Exception ex) 61 | { 62 | // Never let Run throw. Instead, returned a failed HealthCheckResult. 63 | return new HealthCheckResult(new[] 64 | { 65 | new CheckResult 66 | { 67 | Checker = "HealthCheck", 68 | Passed = false, 69 | Output = ex.Message 70 | } 71 | }); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/HealthCheck.Windows/HealthCheck.Windows.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {5A6CF0A0-AFBC-460D-BA51-D5898175983B} 8 | Library 9 | Properties 10 | HealthCheck.Windows 11 | HealthCheck.Windows 12 | v4.5.1 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | Properties\SharedAssemblyInfo.cs 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | {7052554b-0005-47e4-b3c3-318d41261e72} 49 | HealthCheck.Core 50 | 51 | 52 | 53 | 60 | -------------------------------------------------------------------------------- /src/HealthCheck.Nancy/HealthCheck.Nancy.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {971A62B5-6E57-408C-9D2B-A2EA1F9BADFD} 8 | Library 9 | Properties 10 | HealthCheck.Nancy 11 | HealthCheck.Nancy 12 | v4.5.1 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | ..\packages\Nancy.1.1\lib\net40\Nancy.dll 35 | 36 | 37 | 38 | 39 | 40 | 41 | Properties\SharedAssemblyInfo.cs 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | {7052554b-0005-47e4-b3c3-318d41261e72} 52 | HealthCheck.Core 53 | 54 | 55 | 56 | 63 | -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Core.Tests/HealthCheck.Core.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {02AF9FC9-D118-4628-B468-3D77D5DAC257} 8 | Library 9 | Properties 10 | HealthCheck.Core.Tests 11 | HealthCheck.Core.Tests 12 | v4.5.1 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | ..\..\packages\Moq.4.2.1502.0911\lib\net40\Moq.dll 35 | 36 | 37 | ..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | {7052554b-0005-47e4-b3c3-318d41261e72} 58 | HealthCheck.Core 59 | 60 | 61 | 62 | 69 | -------------------------------------------------------------------------------- /src/HealthCheck.Redis/HealthCheck.Redis.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {677553A9-E456-4C94-9777-F8E2030EEE83} 8 | Library 9 | Properties 10 | HealthCheck.Redis 11 | HealthCheck.Redis 12 | v4.5.1 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | ..\packages\StackExchange.Redis.1.0.414\lib\net45\StackExchange.Redis.dll 35 | 36 | 37 | 38 | 39 | 40 | 41 | Properties\SharedAssemblyInfo.cs 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | {7052554b-0005-47e4-b3c3-318d41261e72} 57 | HealthCheck.Core 58 | 59 | 60 | 61 | 62 | 69 | -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Redis.Tests/HealthCheck.Redis.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {87678CED-1200-4B5D-8CC5-B8CAC3FF4229} 8 | Library 9 | Properties 10 | HealthCheck.Redis.Tests 11 | HealthCheck.Redis.Tests 12 | v4.5.1 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | ..\..\packages\Moq.4.2.1502.0911\lib\net40\Moq.dll 35 | 36 | 37 | ..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | {7052554b-0005-47e4-b3c3-318d41261e72} 58 | HealthCheck.Core 59 | 60 | 61 | {677553a9-e456-4c94-9777-f8e2030eee83} 62 | HealthCheck.Redis 63 | 64 | 65 | 66 | 73 | -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Windows.Tests/HealthCheck.Windows.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {81F62453-AB66-4D41-8516-6B4465F49334} 8 | Library 9 | Properties 10 | HealthCheck.Windows.Tests 11 | HealthCheck.Windows.Tests 12 | v4.5.1 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | ..\..\packages\Moq.4.2.1502.0911\lib\net40\Moq.dll 35 | 36 | 37 | ..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | {7052554b-0005-47e4-b3c3-318d41261e72} 58 | HealthCheck.Core 59 | 60 | 61 | {5a6cf0a0-afbc-460d-ba51-d5898175983b} 62 | HealthCheck.Windows 63 | 64 | 65 | 66 | 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studo 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | *_i.c 42 | *_p.c 43 | *_i.h 44 | *.ilk 45 | *.meta 46 | *.obj 47 | *.pch 48 | *.pdb 49 | *.pgc 50 | *.pgd 51 | *.rsp 52 | *.sbr 53 | *.tlb 54 | *.tli 55 | *.tlh 56 | *.tmp 57 | *.tmp_proj 58 | *.log 59 | *.vspscc 60 | *.vssscc 61 | .builds 62 | *.pidb 63 | *.svclog 64 | *.scc 65 | 66 | # Chutzpah Test files 67 | _Chutzpah* 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | *.cachefile 76 | 77 | # Visual Studio profiler 78 | *.psess 79 | *.vsp 80 | *.vspx 81 | 82 | # TFS 2012 Local Workspace 83 | $tf/ 84 | 85 | # Guidance Automation Toolkit 86 | *.gpState 87 | 88 | # ReSharper is a .NET coding add-in 89 | _ReSharper*/ 90 | *.[Rr]e[Ss]harper 91 | *.DotSettings.user 92 | 93 | # JustCode is a .NET coding addin-in 94 | .JustCode 95 | 96 | # TeamCity is a build add-in 97 | _TeamCity* 98 | 99 | # DotCover is a Code Coverage Tool 100 | *.dotCover 101 | 102 | # NCrunch 103 | _NCrunch_* 104 | .*crunch*.local.xml 105 | 106 | # MightyMoose 107 | *.mm.* 108 | AutoTest.Net/ 109 | 110 | # Web workbench (sass) 111 | .sass-cache/ 112 | 113 | # Installshield output folder 114 | [Ee]xpress/ 115 | 116 | # DocProject is a documentation generator add-in 117 | DocProject/buildhelp/ 118 | DocProject/Help/*.HxT 119 | DocProject/Help/*.HxC 120 | DocProject/Help/*.hhc 121 | DocProject/Help/*.hhk 122 | DocProject/Help/*.hhp 123 | DocProject/Help/Html2 124 | DocProject/Help/html 125 | 126 | # Click-Once directory 127 | publish/ 128 | 129 | # Publish Web Output 130 | *.[Pp]ublish.xml 131 | *.azurePubxml 132 | # TODO: Comment the next line if you want to checkin your web deploy settings 133 | # but database connection strings (with potential passwords) will be unencrypted 134 | *.pubxml 135 | *.publishproj 136 | 137 | # NuGet Packages 138 | *.nupkg 139 | # The packages folder can be ignored because of Package Restore 140 | **/packages/* 141 | # except build/, which is used as an MSBuild target. 142 | !**/packages/build/ 143 | # Uncomment if necessary however generally it will be regenerated when needed 144 | #!**/packages/repositories.config 145 | 146 | # Windows Azure Build Output 147 | csx/ 148 | *.build.csdef 149 | 150 | # Windows Store app package directory 151 | AppPackages/ 152 | 153 | # Others 154 | *.[Cc]ache 155 | ClientBin/ 156 | [Ss]tyle[Cc]op.* 157 | ~$* 158 | *~ 159 | *.dbmdl 160 | *.dbproj.schemaview 161 | *.pfx 162 | *.publishsettings 163 | node_modules/ 164 | bower_components/ 165 | 166 | # RIA/Silverlight projects 167 | Generated_Code/ 168 | 169 | # Backup & report files from converting an old project file 170 | # to a newer Visual Studio version. Backup files are not needed, 171 | # because we have git ;-) 172 | _UpgradeReport_Files/ 173 | Backup*/ 174 | UpgradeLog*.XML 175 | UpgradeLog*.htm 176 | 177 | # SQL Server files 178 | *.mdf 179 | *.ldf 180 | 181 | # Business Intelligence projects 182 | *.rdl.data 183 | *.bim.layout 184 | *.bim_*.settings 185 | 186 | # Microsoft Fakes 187 | FakesAssemblies/ 188 | 189 | # Node.js Tools for Visual Studio 190 | .ntvs_analysis.dat 191 | 192 | # Visual Studio 6 build log 193 | *.plg 194 | 195 | # Visual Studio 6 workspace options file 196 | *.opt 197 | -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Nancy.Tests/HealthCheck.Nancy.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {F5C18CB0-A863-4DA7-A99F-1E4AE3B717F2} 8 | Library 9 | Properties 10 | HealthCheck.Nancy.Tests 11 | HealthCheck.Nancy.Tests 12 | v4.5.1 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | ..\..\packages\CsQuery.1.3.3\lib\net40\CsQuery.dll 35 | 36 | 37 | ..\..\packages\Moq.4.2.1502.0911\lib\net40\Moq.dll 38 | 39 | 40 | False 41 | ..\..\packages\Nancy.1.1\lib\net40\Nancy.dll 42 | 43 | 44 | ..\..\packages\Nancy.Testing.1.1\lib\net40\Nancy.Testing.dll 45 | 46 | 47 | ..\..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll 48 | 49 | 50 | ..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | {7052554b-0005-47e4-b3c3-318d41261e72} 71 | HealthCheck.Core 72 | 73 | 74 | {971a62b5-6e57-408c-9d2b-a2ea1f9badfd} 75 | HealthCheck.Nancy 76 | 77 | 78 | 79 | 86 | -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Redis.Tests/Checkers/RedisHasFreeMemoryTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using HealthCheck.Redis.Checkers; 7 | using HealthCheck.Redis.Metrics; 8 | using Moq; 9 | using NUnit.Framework; 10 | 11 | namespace HealthCheck.Redis.Tests.Checkers 12 | { 13 | [TestFixture] 14 | public class RedisHasFreeMemoryTests 15 | { 16 | [Test] 17 | public async Task CheckPassesWhenFreeMemoryIsAboveThreshold() 18 | { 19 | // Arrange 20 | var options = new RedisHasFreeMemory.Options 21 | { 22 | RedisFreeMemoryWarningThresholdInBytes = 1024 23 | }; 24 | long maxMemory = options.RedisFreeMemoryWarningThresholdInBytes * 10; 25 | long usedMemory = maxMemory - options.RedisFreeMemoryWarningThresholdInBytes - 1; 26 | var redisMemoryUsageMock = new Mock(); 27 | redisMemoryUsageMock 28 | .Setup(x => x.Read()) 29 | .ReturnsAsync(usedMemory); 30 | var redisMaxMemoryMock = new Mock(); 31 | redisMaxMemoryMock 32 | .Setup(x => x.Read()) 33 | .ReturnsAsync(maxMemory); 34 | var check = new RedisHasFreeMemory(redisMemoryUsageMock.Object, redisMaxMemoryMock.Object, options); 35 | 36 | // Act 37 | var result = await check.Check(); 38 | 39 | // Assert 40 | Assert.That(result.Passed, Is.True); 41 | } 42 | 43 | [Test] 44 | public async Task CheckFailsWhenFreeMemoryIsBelowThreshold() 45 | { 46 | // Arrange 47 | var options = new RedisHasFreeMemory.Options 48 | { 49 | RedisFreeMemoryWarningThresholdInBytes = 1024 50 | }; 51 | long maxMemory = options.RedisFreeMemoryWarningThresholdInBytes * 10; 52 | long usedMemory = maxMemory - options.RedisFreeMemoryWarningThresholdInBytes + 1; 53 | var redisMemoryUsageMock = new Mock(); 54 | redisMemoryUsageMock 55 | .Setup(x => x.Read()) 56 | .ReturnsAsync(usedMemory); 57 | var redisMaxMemoryMock = new Mock(); 58 | redisMaxMemoryMock 59 | .Setup(x => x.Read()) 60 | .ReturnsAsync(maxMemory); 61 | var check = new RedisHasFreeMemory(redisMemoryUsageMock.Object, redisMaxMemoryMock.Object, options); 62 | 63 | // Act 64 | var result = await check.Check(); 65 | 66 | // Assert 67 | Assert.That(result.Passed, Is.False); 68 | } 69 | 70 | [Test] 71 | public async Task CheckFailsWhenRedisMemoryUsageThrows() 72 | { 73 | // Arrange 74 | var redisMemoryUsageMock = new Mock(); 75 | var exception = new Exception("error message"); 76 | redisMemoryUsageMock.Setup(x => x.Read()).ThrowsAsync(exception); 77 | var redisMaxMemoryMock = new Mock(); 78 | var check = new RedisHasFreeMemory(redisMemoryUsageMock.Object, redisMaxMemoryMock.Object, new RedisHasFreeMemory.Options()); 79 | 80 | // Act 81 | var result = await check.Check(); 82 | 83 | // Assert 84 | Assert.That(result.Passed, Is.False); 85 | Assert.That(result.Output, Is.EqualTo(exception.Message)); 86 | } 87 | 88 | [Test] 89 | public async Task CheckFailsWhenRedisMaxMemoryThrows() 90 | { 91 | // Arrange 92 | var redisMemoryUsageMock = new Mock(); 93 | var exception = new Exception("error message"); 94 | var redisMaxMemoryMock = new Mock(); 95 | redisMaxMemoryMock.Setup(x => x.Read()).ThrowsAsync(exception); 96 | var check = new RedisHasFreeMemory(redisMemoryUsageMock.Object, redisMaxMemoryMock.Object, new RedisHasFreeMemory.Options()); 97 | 98 | // Act 99 | var result = await check.Check(); 100 | 101 | // Assert 102 | Assert.That(result.Passed, Is.False); 103 | Assert.That(result.Output, Is.EqualTo(exception.Message)); 104 | } 105 | 106 | [Test] 107 | public async Task CheckPassesWhenThereIsNoUpperLimitOnMemoryUsage() 108 | { 109 | // Arrange 110 | var redisMaxMemoryMock = new Mock(); 111 | redisMaxMemoryMock.Setup(x => x.Read()).ReturnsAsync(null); 112 | var redisMemoryUsageMock = new Mock(); 113 | redisMemoryUsageMock.Setup(x => x.Read()).ReturnsAsync(1024); 114 | var check = new RedisHasFreeMemory(redisMemoryUsageMock.Object, redisMaxMemoryMock.Object, new RedisHasFreeMemory.Options()); 115 | 116 | // Act 117 | var result = await check.Check(); 118 | 119 | // Assert 120 | Assert.That(result.Passed, Is.True); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/HealthCheck.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthCheck.Core", "HealthCheck.Core\HealthCheck.Core.csproj", "{7052554B-0005-47E4-B3C3-318D41261E72}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthCheck.Redis", "HealthCheck.Redis\HealthCheck.Redis.csproj", "{677553A9-E456-4C94-9777-F8E2030EEE83}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{EBE0C6CC-E4DF-4657-B291-570F25B042E5}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthCheck.Nancy", "HealthCheck.Nancy\HealthCheck.Nancy.csproj", "{971A62B5-6E57-408C-9D2B-A2EA1F9BADFD}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthCheck.Windows", "HealthCheck.Windows\HealthCheck.Windows.csproj", "{5A6CF0A0-AFBC-460D-BA51-D5898175983B}" 15 | EndProject 16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FD23FB63-8010-40AC-B752-D7BD3BBD8D8F}" 17 | ProjectSection(SolutionItems) = preProject 18 | HealthCheck.Core\HealthCheck.Core.nuspec = HealthCheck.Core\HealthCheck.Core.nuspec 19 | HealthCheck.Nancy\HealthCheck.Nancy.nuspec = HealthCheck.Nancy\HealthCheck.Nancy.nuspec 20 | HealthCheck.Redis\HealthCheck.Redis.nuspec = HealthCheck.Redis\HealthCheck.Redis.nuspec 21 | HealthCheck.Windows\HealthCheck.Windows.nuspec = HealthCheck.Windows\HealthCheck.Windows.nuspec 22 | SharedAssemblyInfo.cs = SharedAssemblyInfo.cs 23 | EndProjectSection 24 | EndProject 25 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthCheck.Core.Tests", "Tests\HealthCheck.Core.Tests\HealthCheck.Core.Tests.csproj", "{02AF9FC9-D118-4628-B468-3D77D5DAC257}" 26 | EndProject 27 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthCheck.Windows.Tests", "Tests\HealthCheck.Windows.Tests\HealthCheck.Windows.Tests.csproj", "{81F62453-AB66-4D41-8516-6B4465F49334}" 28 | EndProject 29 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthCheck.Redis.Tests", "Tests\HealthCheck.Redis.Tests\HealthCheck.Redis.Tests.csproj", "{87678CED-1200-4B5D-8CC5-B8CAC3FF4229}" 30 | EndProject 31 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthCheck.Nancy.Tests", "Tests\HealthCheck.Nancy.Tests\HealthCheck.Nancy.Tests.csproj", "{F5C18CB0-A863-4DA7-A99F-1E4AE3B717F2}" 32 | EndProject 33 | Global 34 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 35 | Debug|Any CPU = Debug|Any CPU 36 | Release|Any CPU = Release|Any CPU 37 | EndGlobalSection 38 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 39 | {7052554B-0005-47E4-B3C3-318D41261E72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {7052554B-0005-47E4-B3C3-318D41261E72}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {7052554B-0005-47E4-B3C3-318D41261E72}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {7052554B-0005-47E4-B3C3-318D41261E72}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {677553A9-E456-4C94-9777-F8E2030EEE83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {677553A9-E456-4C94-9777-F8E2030EEE83}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {677553A9-E456-4C94-9777-F8E2030EEE83}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {677553A9-E456-4C94-9777-F8E2030EEE83}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {971A62B5-6E57-408C-9D2B-A2EA1F9BADFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 48 | {971A62B5-6E57-408C-9D2B-A2EA1F9BADFD}.Debug|Any CPU.Build.0 = Debug|Any CPU 49 | {971A62B5-6E57-408C-9D2B-A2EA1F9BADFD}.Release|Any CPU.ActiveCfg = Release|Any CPU 50 | {971A62B5-6E57-408C-9D2B-A2EA1F9BADFD}.Release|Any CPU.Build.0 = Release|Any CPU 51 | {5A6CF0A0-AFBC-460D-BA51-D5898175983B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 52 | {5A6CF0A0-AFBC-460D-BA51-D5898175983B}.Debug|Any CPU.Build.0 = Debug|Any CPU 53 | {5A6CF0A0-AFBC-460D-BA51-D5898175983B}.Release|Any CPU.ActiveCfg = Release|Any CPU 54 | {5A6CF0A0-AFBC-460D-BA51-D5898175983B}.Release|Any CPU.Build.0 = Release|Any CPU 55 | {02AF9FC9-D118-4628-B468-3D77D5DAC257}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 56 | {02AF9FC9-D118-4628-B468-3D77D5DAC257}.Debug|Any CPU.Build.0 = Debug|Any CPU 57 | {02AF9FC9-D118-4628-B468-3D77D5DAC257}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {02AF9FC9-D118-4628-B468-3D77D5DAC257}.Release|Any CPU.Build.0 = Release|Any CPU 59 | {81F62453-AB66-4D41-8516-6B4465F49334}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 60 | {81F62453-AB66-4D41-8516-6B4465F49334}.Debug|Any CPU.Build.0 = Debug|Any CPU 61 | {81F62453-AB66-4D41-8516-6B4465F49334}.Release|Any CPU.ActiveCfg = Release|Any CPU 62 | {81F62453-AB66-4D41-8516-6B4465F49334}.Release|Any CPU.Build.0 = Release|Any CPU 63 | {87678CED-1200-4B5D-8CC5-B8CAC3FF4229}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 64 | {87678CED-1200-4B5D-8CC5-B8CAC3FF4229}.Debug|Any CPU.Build.0 = Debug|Any CPU 65 | {87678CED-1200-4B5D-8CC5-B8CAC3FF4229}.Release|Any CPU.ActiveCfg = Release|Any CPU 66 | {87678CED-1200-4B5D-8CC5-B8CAC3FF4229}.Release|Any CPU.Build.0 = Release|Any CPU 67 | {F5C18CB0-A863-4DA7-A99F-1E4AE3B717F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 68 | {F5C18CB0-A863-4DA7-A99F-1E4AE3B717F2}.Debug|Any CPU.Build.0 = Debug|Any CPU 69 | {F5C18CB0-A863-4DA7-A99F-1E4AE3B717F2}.Release|Any CPU.ActiveCfg = Release|Any CPU 70 | {F5C18CB0-A863-4DA7-A99F-1E4AE3B717F2}.Release|Any CPU.Build.0 = Release|Any CPU 71 | EndGlobalSection 72 | GlobalSection(SolutionProperties) = preSolution 73 | HideSolutionNode = FALSE 74 | EndGlobalSection 75 | GlobalSection(NestedProjects) = preSolution 76 | {02AF9FC9-D118-4628-B468-3D77D5DAC257} = {EBE0C6CC-E4DF-4657-B291-570F25B042E5} 77 | {81F62453-AB66-4D41-8516-6B4465F49334} = {EBE0C6CC-E4DF-4657-B291-570F25B042E5} 78 | {87678CED-1200-4B5D-8CC5-B8CAC3FF4229} = {EBE0C6CC-E4DF-4657-B291-570F25B042E5} 79 | {F5C18CB0-A863-4DA7-A99F-1E4AE3B717F2} = {EBE0C6CC-E4DF-4657-B291-570F25B042E5} 80 | EndGlobalSection 81 | EndGlobal 82 | -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Nancy.Tests/HealthCheckModuleTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using HealthCheck.Core; 7 | using Moq; 8 | using Nancy; 9 | using Nancy.Testing; 10 | using Newtonsoft.Json; 11 | using NUnit.Framework; 12 | 13 | namespace HealthCheck.Nancy.Tests 14 | { 15 | [TestFixture] 16 | public class HealthCheckModuleTests 17 | { 18 | [Test] 19 | public void UsesTheDesiredRoute() 20 | { 21 | // Arrange 22 | var options = new HealthCheckOptions 23 | { 24 | Route = "/my/custom/healthcheck" 25 | }; 26 | var module = new HealthCheckModule(Enumerable.Empty(), options); 27 | var bootstrapper = new ConfigurableBootstrapper(with => 28 | { 29 | with.Module(module); 30 | }); 31 | var browser = new Browser(bootstrapper); 32 | 33 | // Act 34 | var response = browser.Get(options.Route, with => 35 | { 36 | with.HttpsRequest(); 37 | }); 38 | 39 | // Assert 40 | Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); 41 | } 42 | 43 | [Test] 44 | public void CanRequireHttps() 45 | { 46 | // Arrange 47 | var options = new HealthCheckOptions 48 | { 49 | RequireHttps = true 50 | }; 51 | var module = new HealthCheckModule(Enumerable.Empty(), options); 52 | var bootstrapper = new ConfigurableBootstrapper(with => 53 | { 54 | with.Module(module); 55 | }); 56 | var browser = new Browser(bootstrapper); 57 | 58 | // Act 59 | var response = browser.Get("/healthcheck", with => 60 | { 61 | with.HttpRequest(); 62 | }); 63 | 64 | // Assert 65 | Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Forbidden)); 66 | } 67 | 68 | [Test] 69 | public void DoesNotHaveToRequireHttps() 70 | { 71 | // Arrange 72 | var options = new HealthCheckOptions 73 | { 74 | RequireHttps = false 75 | }; 76 | var module = new HealthCheckModule(Enumerable.Empty(), options); 77 | var bootstrapper = new ConfigurableBootstrapper(with => 78 | { 79 | with.Module(module); 80 | }); 81 | var browser = new Browser(bootstrapper); 82 | 83 | // Act 84 | var response = browser.Get("/healthcheck", with => 85 | { 86 | with.HttpRequest(); 87 | }); 88 | 89 | // Assert 90 | Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); 91 | } 92 | 93 | [Test] 94 | public void AllRequestsAuthorizedByDefault() 95 | { 96 | // Arrange 97 | var module = new HealthCheckModule(Enumerable.Empty()); 98 | var bootstrapper = new ConfigurableBootstrapper(with => 99 | { 100 | with.Module(module); 101 | }); 102 | var browser = new Browser(bootstrapper); 103 | 104 | // Act 105 | var response = browser.Get("/healthcheck", with => 106 | { 107 | with.HttpsRequest(); 108 | }); 109 | 110 | // Assert 111 | Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); 112 | } 113 | 114 | [Test] 115 | public void BlocksUnauthorizedRequests() 116 | { 117 | // Arrange 118 | var options = new HealthCheckOptions 119 | { 120 | AuthorizationCallback = context => Task.FromResult(false) 121 | }; 122 | var module = new HealthCheckModule(Enumerable.Empty(), options); 123 | var bootstrapper = new ConfigurableBootstrapper(with => 124 | { 125 | with.Module(module); 126 | }); 127 | var browser = new Browser(bootstrapper); 128 | 129 | // Act 130 | var response = browser.Get("/healthcheck", with => 131 | { 132 | with.HttpsRequest(); 133 | }); 134 | 135 | // Assert 136 | Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized)); 137 | } 138 | 139 | [Test] 140 | public void RunsAllCheckers() 141 | { 142 | // Arrange 143 | var checkerMocks = Enumerable 144 | .Range(1, 3) 145 | .Select(x => 146 | { 147 | var mock = new Mock(); 148 | mock.Setup(y => y.Check()).ReturnsAsync(new CheckResult()); 149 | return mock; 150 | }) 151 | .ToArray(); 152 | var module = new HealthCheckModule(checkerMocks.Select(x => x.Object)); 153 | var bootstrapper = new ConfigurableBootstrapper(with => 154 | { 155 | with.Module(module); 156 | }); 157 | var browser = new Browser(bootstrapper); 158 | 159 | // Act 160 | var response = browser.Get("/healthcheck", with => 161 | { 162 | with.HttpsRequest(); 163 | }); 164 | 165 | // Assert 166 | Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); 167 | foreach (var checkerMock in checkerMocks) 168 | { 169 | checkerMock.Verify(x => x.Check(), Times.Once); 170 | } 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HealthCheck - flexible, asynchronous health checks for your .NET apps 2 | 3 | [![Build status](https://img.shields.io/appveyor/ci/hihaj/healthcheck/master.svg)](https://ci.appveyor.com/project/Hihaj/healthcheck/branch/master) 4 | [![Lastest NuGet package version](https://img.shields.io/nuget/v/Hihaj.HealthCheck.svg)](https://www.nuget.org/packages?q=hihaj.healthcheck) 5 | 6 | Heavily inspired by [Runscope/healthcheck](https://github.com/Runscope/healthcheck), HealthCheck provides a simple but flexible way to monitor application health, including OS resources, third-party service status etc. 7 | 8 | 9 | ## High-level overview 10 | 11 | A *health check* runs one or more independent *checks* that either pass or not depending on some specific *metric(s)*. The health check as a whole is successful if (and only if) all checks pass. 12 | 13 | A conceptual example could be to require that CPU usage is below 80% and Redis is available. 14 | 15 | 16 | ## Getting started 17 | 18 | HealthCheck can be used in any type of .NET application - just create an instance of `HealthCheck`, add some `IChecker` implementations and run it. A simple example that uses a built-in checker from the `Hihaj.HealthCheck.Windows` package: 19 | 20 | ```c# 21 | var healthCheck = new HealthCheck(new IChecker[] 22 | { 23 | new SystemDriveHasFreeSpace(new AvailableSystemDriveSpace()) 24 | }); 25 | var result = await healthCheck.Run(); 26 | 27 | Console.WriteLine(result.Passed); // true/false 28 | Console.WriteLine(result.Status); // "success"/"failure" 29 | foreach (var checkResult in result.Results) 30 | { 31 | Console.WriteLine(checkResult.Checker); // E.g. "System drive has free space" 32 | Console.WriteLine(checkResult.Passed); // true/false 33 | Console.WriteLine(checkResult.Output); // E.g. "123.4 GB available" 34 | } 35 | ``` 36 | 37 | ### Bonus: Nancy health check module 38 | 39 | If you're building a [Nancy](https://github.com/NancyFx) web app, things are even simpler. Just install the `Hihaj.HealthCheck.Nancy` package and add the `HealthCheckModule` to the `ConfigureApplicationContainer` method of your bootstrapper: 40 | 41 | ```c# 42 | container.RegisterMultiple(new[] 43 | { 44 | typeof(SystemDriveHasFreeSpace) 45 | }); 46 | container.Register(); 47 | ``` 48 | 49 | This will expose the health check on the default url (`/healthcheck`), require https and use default options for all `IChecker` implementations that you specified. 50 | 51 | If you need more control, you can register non-default options: 52 | 53 | ```c# 54 | container.Register(new SystemDriveHasFreeSpace.Options 55 | { 56 | SystemDriveAvailableFreeSpaceWarningThresholdInBytes = 57 | 5L * 1024L * 1024L * 1024L; // 5 GB 58 | }); 59 | container.RegisterMultiple(new[] 60 | { 61 | typeof(SystemDriveHasFreeSpace) 62 | }); 63 | container.Register(new HealthCheckOptions 64 | { 65 | Route = "/healthcheck", 66 | RequireHttps = true, 67 | AuthorizationCallback = context => Task.FromResult(true) 68 | }); 69 | container.Register(); 70 | ``` 71 | 72 | When issuing a GET request for the health check url (using the configured route or the default `/healthcheck` if none provided), you get back the complete result of the health check in JSON format. For example: 73 | 74 | ```json 75 | { 76 | "results": [ 77 | { 78 | "checker": "CPU usage is OK", 79 | "passed": true, 80 | "output": "Currently using 4,2 % of total processor time." 81 | }, 82 | { 83 | "checker": "Redis has free memory", 84 | "passed": true, 85 | "output": "1 % of the reserved memory is used (507,8 MB free)." 86 | }, 87 | { 88 | "checker": "Redis is available", 89 | "passed": true, 90 | "output": "Version 2.8.19, uptime 1 days 12 hours 51 minutes." 91 | }, 92 | { 93 | "checker": "System drive has free space", 94 | "passed": true, 95 | "output": "84,9 GB available." 96 | } 97 | ], 98 | "passed": true, 99 | "status": "success" 100 | } 101 | ``` 102 | 103 | This endpoint can easily be monitored continously by a tool or service such as [Runscope](https://www.runscope.com/) (in fact, the JSON output is compatible with the one produced by [Runscope/healthcheck](https://github.com/Runscope/healthcheck)). 104 | 105 | 106 | ## Creating a custom `IChecker` 107 | 108 | Implement the `IChecker` interface to define custom checks: 109 | 110 | ```c# 111 | public interface IChecker 112 | { 113 | string Name { get; } 114 | Task Check(); 115 | } 116 | ``` 117 | 118 | The `CheckResult` returned by the `Check` method is very simple: 119 | 120 | ```c# 121 | public class CheckResult 122 | { 123 | public string Checker { get; set; } // Display name of the check 124 | public bool Passed { get; set; } // Whether it passed or not 125 | public string Output { get; set; } // Description of the outcome 126 | } 127 | ``` 128 | 129 | You can of course implement your checks in any way you like, but to improve testability you could base the logic on readings from one or more _metrics_: 130 | 131 | ```c# 132 | public interface IMetric 133 | { 134 | Task Read(); 135 | } 136 | ``` 137 | 138 | A simple example: 139 | 140 | ```c# 141 | public interface ITemperature : IMetric 142 | { 143 | } 144 | 145 | public class Temperature : ITemperature 146 | { 147 | public async Task Read() 148 | { 149 | // Read the current temperature from some source, 150 | // preferably in an asynchronous manner. 151 | // ... 152 | } 153 | } 154 | 155 | public class TemperatureIsBelowFreezingPoint : IChecker 156 | { 157 | private readonly ITemperature _temperature; 158 | 159 | public string Name 160 | { 161 | get { return "Temperature is below freezing point"; } 162 | } 163 | 164 | public TemperatureIsBelowFreezingPoint(ITemperature temperature) 165 | { 166 | _temperature = temperature; 167 | } 168 | 169 | public async Task Check() 170 | { 171 | var temp = await _temperature.Read().ConfigureAwait(false); 172 | return new CheckResult 173 | { 174 | Checker = Name, 175 | Passed = temp <= 0, 176 | Output = string.Format("Current temperature is {0:F1} °C.", temp) 177 | }; 178 | } 179 | } 180 | ``` 181 | 182 | 183 | ## Built-in `IChecker`s 184 | 185 | ### Hihaj.HealthCheck.Windows 186 | 187 | - `CpuUsageIsOk` - passes as long as CPU usage is below a (configurable) threshold. _(Uses performance counters, which means the Windows user that is executing the health check (such as an app pool identity) needs to have the correct permissions.)_ 188 | - `SystemDriveHasFreeSpace` - passes as long as the system drive has at least X bytes available (configurable). 189 | 190 | ### Hihaj.HealthCheck.Redis 191 | 192 | - `RedisIsAvailable` - self-explanatory. 193 | - `RedisHasFreeMemory` - passes as long as Redis has more than a (configurable) amount of free memory (or no upper limit). 194 | 195 | *Note:* This package depends on [StackExchange.Redis](https://github.com/StackExchange/StackExchange.Redis). 196 | -------------------------------------------------------------------------------- /src/Tests/HealthCheck.Core.Tests/HealthCheckTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Moq; 7 | using NUnit.Framework; 8 | 9 | namespace HealthCheck.Core.Tests 10 | { 11 | [TestFixture] 12 | public class HealthCheckTests 13 | { 14 | [Test] 15 | public async Task Run_WhenNullCheckers_SuccessResult() 16 | { 17 | // Arrange 18 | var healthCheck = new HealthCheck(null); 19 | 20 | // Act 21 | var result = await healthCheck.Run(); 22 | 23 | // Assert 24 | Assert.That(result.Passed, Is.True); 25 | Assert.That(result.Status, Is.EqualTo("success")); 26 | Assert.That(result.Results, Is.Empty); 27 | } 28 | 29 | [Test] 30 | public async Task Run_WhenNoCheckers_SuccessResult() 31 | { 32 | // Arrange 33 | var healthCheck = new HealthCheck(Enumerable.Empty()); 34 | 35 | // Act 36 | var result = await healthCheck.Run(); 37 | 38 | // Assert 39 | Assert.That(result.Passed, Is.True); 40 | Assert.That(result.Status, Is.EqualTo("success")); 41 | Assert.That(result.Results, Is.Empty); 42 | } 43 | 44 | [Test] 45 | public async Task Run_RunsAllCheckers() 46 | { 47 | // Arrange 48 | var checkerMocks = Enumerable 49 | .Range(1, 3) 50 | .Select(x => 51 | { 52 | var mock = new Mock(); 53 | mock.SetupGet(y => y.Name).Returns(x.ToString()); 54 | mock.Setup(y => y.Check()).ReturnsAsync(new CheckResult()); 55 | return mock; 56 | }) 57 | .ToArray(); 58 | var healthCheck = new HealthCheck(checkerMocks.Select(x => x.Object)); 59 | 60 | // Act 61 | await healthCheck.Run(); 62 | 63 | // Assert 64 | foreach (var checkerMock in checkerMocks) 65 | { 66 | checkerMock.Verify(x => x.Check(), Times.Once); 67 | } 68 | } 69 | 70 | [Test] 71 | public async Task Run_WhenCheckerThrows_FailureResult() 72 | { 73 | // Arrange 74 | var checkerMocks = Enumerable 75 | .Range(1, 3) 76 | .Select(x => 77 | { 78 | var mock = new Mock(); 79 | mock.SetupGet(y => y.Name).Returns(x.ToString()); 80 | mock.Setup(y => y.Check()).ThrowsAsync(new Exception("error " + mock.Object.Name)); 81 | return mock; 82 | }) 83 | .ToArray(); 84 | var healthCheck = new HealthCheck(checkerMocks.Select(x => x.Object)); 85 | 86 | // Act 87 | var result = await healthCheck.Run(); 88 | 89 | // Assert 90 | Assert.That(result.Passed, Is.False); 91 | Assert.That(result.Status, Is.EqualTo("failure")); 92 | Assert.That(result.Results.Length, Is.EqualTo(checkerMocks.Length)); 93 | foreach (var checkerMock in checkerMocks) 94 | { 95 | var checkResult = result.Results.Single(x => x.Checker == checkerMock.Object.Name); 96 | Assert.That(checkResult.Checker, Is.EqualTo(checkerMock.Object.Name)); 97 | Assert.That(checkResult.Passed, Is.False); 98 | Assert.That(checkResult.Output, Is.EqualTo("error " + checkerMock.Object.Name)); 99 | } 100 | } 101 | 102 | [Test] 103 | public async Task Run_WhenAtLeastOneCheckerFails_FailureResult() 104 | { 105 | // Arrange 106 | var checkerMocks = Enumerable 107 | .Range(1, 3) 108 | .Select(x => 109 | { 110 | var mock = new Mock(); 111 | mock.SetupGet(y => y.Name).Returns(x.ToString()); 112 | mock.Setup(y => y.Check()).ReturnsAsync(new CheckResult { Passed = true }); 113 | return mock; 114 | }) 115 | .ToArray(); 116 | var exception = new Exception("error message"); 117 | checkerMocks[1].Setup(x => x.Check()).ThrowsAsync(exception); 118 | var healthCheck = new HealthCheck(checkerMocks.Select(x => x.Object)); 119 | 120 | // Act 121 | var result = await healthCheck.Run(); 122 | 123 | // Assert 124 | Assert.That(result.Passed, Is.False); 125 | Assert.That(result.Status, Is.EqualTo("failure")); 126 | Assert.That(result.Results.Length, Is.EqualTo(checkerMocks.Length)); 127 | Assert.That(result.Results.Count(x => x.Passed), Is.EqualTo(checkerMocks.Length - 1)); 128 | } 129 | 130 | [Test] 131 | public async Task Run_WhenAllCheckersPass_SuccessResult() 132 | { 133 | // Arrange 134 | var checkerMocks = Enumerable 135 | .Range(1, 3) 136 | .Select(x => 137 | { 138 | var mock = new Mock(); 139 | mock.SetupGet(y => y.Name).Returns(x.ToString()); 140 | mock.Setup(y => y.Check()).ReturnsAsync(new CheckResult { Passed = true }); 141 | return mock; 142 | }) 143 | .ToArray(); 144 | var healthCheck = new HealthCheck(checkerMocks.Select(x => x.Object)); 145 | 146 | // Act 147 | var result = await healthCheck.Run(); 148 | 149 | // Assert 150 | Assert.That(result.Passed, Is.True); 151 | Assert.That(result.Status, Is.EqualTo("success")); 152 | Assert.That(result.Results.Length, Is.EqualTo(checkerMocks.Length)); 153 | Assert.That(result.Results.Count(x => x.Passed), Is.EqualTo(checkerMocks.Length)); 154 | } 155 | 156 | [Test] 157 | public async Task Run_WhenSomethingOutsideOfTheCheckersThrows_FailureResult() 158 | { 159 | // Arrange 160 | var checkersMock = new Mock>(); 161 | var exception = new Exception("surprise"); 162 | checkersMock.Setup(x => x.GetEnumerator()).Throws(exception); 163 | var healthCheck = new HealthCheck(checkersMock.Object); 164 | 165 | // Act 166 | var result = await healthCheck.Run(); 167 | 168 | // Assert 169 | Assert.That(result.Passed, Is.False); 170 | Assert.That(result.Status, Is.EqualTo("failure")); 171 | Assert.That(result.Results, Is.Not.Null); 172 | Assert.That(result.Results.Single().Checker, Is.EqualTo("HealthCheck")); 173 | Assert.That(result.Results.Single().Passed, Is.False); 174 | Assert.That(result.Results.Single().Output, Is.EqualTo(exception.Message)); 175 | } 176 | } 177 | } 178 | --------------------------------------------------------------------------------