using System.Net; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using ZB.MOM.WW.Health; namespace ZB.MOM.WW.Health.Tests; /// /// Verifies : a decorated endpoint serves /// normally (200) when the resolved reports the node active, and /// returns 503 with a Retry-After header when the node is a standby. /// public sealed class ActiveNodeGateTests { private sealed class FakeActiveNodeGate : IActiveNodeGate { public bool IsActiveNode { get; set; } } private static async Task CallAsync(bool isActive) { var builder = WebApplication.CreateBuilder(); builder.WebHost.UseTestServer(); builder.Services.AddSingleton(new FakeActiveNodeGate { IsActiveNode = isActive }); await using var app = builder.Build(); app.MapGet("/x", () => "ok").RequireActiveNode(); await app.StartAsync(); var client = app.GetTestClient(); return await client.GetAsync("/x"); } [Fact] public async Task ActiveNode_Returns200() { var response = await CallAsync(isActive: true); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("ok", await response.Content.ReadAsStringAsync()); } [Fact] public async Task StandbyNode_Returns503_WithRetryAfterHeader() { var response = await CallAsync(isActive: false); Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode); Assert.True( response.Headers.Contains("Retry-After"), "Standby response must carry a Retry-After header."); } [Fact] public async Task NoGateRegistered_AllowsRequest() { // When no IActiveNodeGate is registered (non-clustered host / tests), the endpoint is served. var builder = WebApplication.CreateBuilder(); builder.WebHost.UseTestServer(); await using var app = builder.Build(); app.MapGet("/x", () => "ok").RequireActiveNode(); await app.StartAsync(); var response = await app.GetTestClient().GetAsync("/x"); Assert.Equal(HttpStatusCode.OK, response.StatusCode); } }