Files
GmRelayBot/tests/GmRelay.Bot.Tests/Web/WebHealthEndpointTests.cs
Toutsu 105b3c59d7
PR Checks / test-and-build (pull_request) Successful in 8m34s
fix: address review feedback for health check endpoints
- Install wget in Web Dockerfile for compose healthcheck
- Ensure HttpListener response is always closed in BotHealthCheckHostedService
- Use ephemeral port in Bot health check test to avoid port conflicts
- Rename NpgsqlHealthCheck test to reflect actual behavior

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 11:16:58 +03:00

87 lines
3.0 KiB
C#

using GmRelay.Web.Health;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Npgsql;
using System.Net;
using System.Text.Json;
namespace GmRelay.Bot.Tests.Web;
public sealed class WebHealthEndpointTests
{
private static WebApplication CreateTestApp(HealthStatus npgsqlStatus)
{
var builder = WebApplication.CreateBuilder();
builder.WebHost.UseTestServer();
builder.Services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"])
.AddCheck("npgsql", () => new HealthCheckResult(npgsqlStatus));
var app = builder.Build();
app.MapHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = async (context, report) =>
{
context.Response.ContentType = "application/json";
var response = new
{
status = report.Status == HealthStatus.Healthy ? "healthy" : "unhealthy",
timestamp = DateTimeOffset.UtcNow.ToString("O")
};
await context.Response.WriteAsJsonAsync(response);
}
});
app.MapHealthChecks("/alive", new HealthCheckOptions
{
Predicate = r => r.Tags.Contains("live")
});
return app;
}
[Fact]
public async Task HealthEndpoint_ShouldReturn200AndJson_WhenHealthy()
{
await using var app = CreateTestApp(HealthStatus.Healthy);
await app.StartAsync();
using var client = app.GetTestClient();
var response = await client.GetAsync("/health");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var content = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(content);
Assert.Equal("healthy", doc.RootElement.GetProperty("status").GetString());
Assert.NotNull(doc.RootElement.GetProperty("timestamp").GetString());
}
[Fact]
public async Task HealthEndpoint_ShouldReturn503_WhenDatabaseUnavailable()
{
await using var app = CreateTestApp(HealthStatus.Unhealthy);
await app.StartAsync();
using var client = app.GetTestClient();
var response = await client.GetAsync("/health");
Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
}
[Fact]
public async Task NpgsqlHealthCheck_ShouldReturnUnhealthy_WhenDatabaseIsInaccessible()
{
var dataSource = NpgsqlDataSource.Create("Host=localhost;Port=5432;Database=gmrelay_db;Username=gmrelay;Password=fake");
var healthCheck = new NpgsqlHealthCheck(dataSource);
var result = await healthCheck.CheckHealthAsync(new HealthCheckContext());
Assert.Equal(HealthStatus.Unhealthy, result.Status);
Assert.NotNull(result.Exception);
}
}