105b3c59d7
PR Checks / test-and-build (pull_request) Successful in 8m34s
- 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>
87 lines
3.0 KiB
C#
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);
|
|
}
|
|
}
|