< Summary

Information
Class: Chronicis.Api.Controllers.HealthController
Assembly: Chronicis.Api
File(s): /home/runner/work/chronicis/chronicis/src/Chronicis.Api/Controllers/HealthController.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 72
Coverable lines: 72
Total lines: 149
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 16
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
GetHealth()100%210%
GetReadiness()0%2040%
GetSystemStatus()0%4260%
MaskConnectionString(...)0%4260%

File(s)

/home/runner/work/chronicis/chronicis/src/Chronicis.Api/Controllers/HealthController.cs

#LineLine coverage
 1using Chronicis.Api.Data;
 2using Chronicis.Api.Services;
 3using Chronicis.Shared.DTOs;
 4using Microsoft.AspNetCore.Mvc;
 5using Microsoft.Data.SqlClient;
 6using Microsoft.EntityFrameworkCore;
 7
 8namespace Chronicis.Api.Controllers;
 9
 10/// <summary>
 11/// API endpoints for health checks.
 12/// These endpoints do NOT require authentication.
 13/// </summary>
 14[ApiController]
 15[Route("health")]
 16public class HealthController : ControllerBase
 17{
 18    private readonly ChronicisDbContext _context;
 19    private readonly ILogger<HealthController> _logger;
 20    private readonly IConfiguration _configuration;
 21    private readonly ISystemHealthService _systemHealthService;
 22
 023    public HealthController(
 024        ChronicisDbContext context,
 025        ILogger<HealthController> logger,
 026        IConfiguration configuration,
 027        ISystemHealthService systemHealthService)
 28    {
 029        _context = context;
 030        _logger = logger;
 031        _configuration = configuration;
 032        _systemHealthService = systemHealthService;
 033    }
 34
 35    /// <summary>
 36    /// GET /api/health - Basic health check endpoint.
 37    /// Returns 200 OK if the API is running.
 38    /// </summary>
 39    [HttpGet]
 40    public IActionResult GetHealth()
 41    {
 042        _logger.LogInformation("Health Endpoint Called");
 043        return Ok(new
 044        {
 045            status = "healthy",
 046            timestamp = DateTime.UtcNow
 047        });
 48    }
 49
 50    /// <summary>
 51    /// GET /api/health/ready - Readiness check including database connectivity.
 52    /// Returns 200 OK if the API and database are ready.
 53    /// </summary>
 54    [HttpGet("ready")]
 55    public async Task<IActionResult> GetReadiness()
 56    {
 57        try
 58        {
 59            // Check database connectivity
 060            var canConnect = await _context.Database.CanConnectAsync();
 61
 062            if (!canConnect)
 63            {
 064                _logger.LogWarning("Health check failed: Cannot connect to database");
 065                return StatusCode(503, new
 066                {
 067                    status = "unhealthy",
 068                    timestamp = DateTime.UtcNow,
 069                    checks = new
 070                    {
 071                        database = "unavailable"
 072                    }
 073                });
 74            }
 75
 76            // Get connection string info for diagnostics (mask password)
 077            var connStr = _configuration.GetConnectionString("ChronicisDb") ?? "";
 078            var maskedConnStr = MaskConnectionString(connStr);
 79
 80
 081            _logger.LogInformation("Readiness endpoint succeeded");
 82
 083            return Ok(new
 084            {
 085                status = "healthy",
 086                timestamp = DateTime.UtcNow,
 087                checks = new
 088                {
 089                    database = "connected",
 090                    connectionInfo = maskedConnStr
 091                }
 092            });
 93        }
 094        catch (Exception ex)
 95        {
 096            _logger.LogError(ex, "Health check failed with exception");
 097            return StatusCode(503, new
 098            {
 099                status = "unhealthy",
 0100                timestamp = DateTime.UtcNow,
 0101                error = ex.Message
 0102            });
 103        }
 0104    }
 105
 106    /// <summary>
 107    /// GET /api/health/status - Comprehensive system health status.
 108    /// Returns the health status of all system dependencies.
 109    /// </summary>
 110    [HttpGet("status")]
 111    public async Task<ActionResult<SystemHealthStatusDto>> GetSystemStatus()
 112    {
 0113        _logger.LogInformation("System health status endpoint called");
 114
 0115        var systemHealth = await _systemHealthService.GetSystemHealthAsync();
 116
 117        // Return appropriate HTTP status code based on overall health
 0118        var statusCode = systemHealth.OverallStatus switch
 0119        {
 0120            HealthStatus.Healthy => 200,
 0121            HealthStatus.Degraded => 200, // Still operational
 0122            HealthStatus.Unhealthy => 503,
 0123            _ => 200
 0124        };
 125
 0126        return StatusCode(statusCode, systemHealth);
 0127    }
 128
 129    private static string MaskConnectionString(string connectionString)
 130    {
 0131        if (string.IsNullOrEmpty(connectionString))
 0132            return "(empty)";
 133
 134        try
 135        {
 0136            var builder = new SqlConnectionStringBuilder(connectionString);
 0137            var hasPassword = !string.IsNullOrEmpty(builder.Password);
 0138            var hasUserId = !string.IsNullOrEmpty(builder.UserID);
 139
 0140            return $"Server={builder.DataSource}; Database={builder.InitialCatalog}; " +
 0141                   $"User={(!hasUserId ? "(none)" : "****")}; Password={(!hasPassword ? "(none)" : "****")}; " +
 0142                   $"MARS={builder.MultipleActiveResultSets}; Encrypt={builder.Encrypt}";
 143        }
 0144        catch
 145        {
 0146            return "(invalid connection string format)";
 147        }
 0148    }
 149}