using System.Net; using System.Net.Http; using System.Text.Json; using Azaion.Missions.E2E.Fixtures; using Azaion.Missions.E2E.Helpers; using Xunit; namespace Azaion.Missions.E2E.Tests.Health; /// /// FT-P-16 (anonymous 200) and FT-P-17 (200 with PG stopped). FT-P-17 is a /// SkippableFact: it runs only when COMPOSE_RESTART_ENABLED=1 and the e2e /// container has docker CLI access; otherwise it skips with a clear reason. /// Traces: AC-7.1, AC-7.2, AC-7.3. /// [Collection("Health")] [Trait("Category", "Blackbox")] public sealed class HealthTests : TestBase, IClassFixture { private readonly PostgresStopStartFixture _pg; public HealthTests(PostgresStopStartFixture pg) => _pg = pg; [Fact] [Trait("Traces", "AC-7.1")] [Trait("max_ms", "2000")] public async Task FT_P_16_health_returns_200_anonymous_with_lowercase_status_key() { // Arrange using var http = new HttpRequestMessage(HttpMethod.Get, "/health"); // Explicitly NO Authorization header — health is anonymous. // Act using var response = await Missions.SendAsync(http); // Assert await HttpAssertions.AssertStatusAsync(response, HttpStatusCode.OK); var raw = await response.Content.ReadAsStringAsync(); using var doc = JsonDocument.Parse(raw); var root = doc.RootElement; // The anonymous-object literal in Program.cs declares the key as // lowercase "status"; assert that exact contract — a future global // PascalCase shift would break consumers. Assert.True(root.TryGetProperty("status", out var statusEl), $"missing 'status' key: {raw}"); Assert.Equal("healthy", statusEl.GetString()); // Reject any extra keys to pin the envelope. var extras = root.EnumerateObject().Select(p => p.Name) .Where(n => n != "status").ToArray(); Assert.True(extras.Length == 0, $"unexpected extra keys in /health body: {string.Join(",", extras)}"); } [SkippableFact] [Trait("Traces", "AC-7.2,AC-7.3")] [Trait("max_ms", "5000")] public async Task FT_P_17_health_returns_200_with_postgres_stopped_proves_no_db_ping() { Skip.IfNot(_pg.Enabled, "PostgresStopStartFixture disabled (COMPOSE_RESTART_ENABLED!=1). " + "Enable in CI; locally this scenario requires docker socket access."); // Arrange _pg.Stop(); try { using var http = new HttpRequestMessage(HttpMethod.Get, "/health"); // Act using var response = await Missions.SendAsync(http); // Assert await HttpAssertions.AssertStatusAsync(response, HttpStatusCode.OK); var raw = await response.Content.ReadAsStringAsync(); using var doc = JsonDocument.Parse(raw); Assert.Equal("healthy", doc.RootElement.GetProperty("status").GetString()); } finally { _pg.Start(); } } }