using System; using System.Net; using System.Net.Http; using System.Threading.Tasks; using Shouldly; using Xunit; using ZB.MOM.WW.LmxOpcUa.Host.Status; using ZB.MOM.WW.LmxOpcUa.Tests.Helpers; namespace ZB.MOM.WW.LmxOpcUa.Tests.Status { /// /// Verifies the lightweight HTTP dashboard host that exposes bridge status to operators. /// public class StatusWebServerTests : IDisposable { private readonly StatusWebServer _server; private readonly HttpClient _client; private readonly int _port; /// /// Starts a status web server on a random test port and prepares an HTTP client for endpoint assertions. /// public StatusWebServerTests() { _port = new Random().Next(18000, 19000); var reportService = new StatusReportService(new HealthCheckService(), 10); var mxClient = new FakeMxAccessClient(); reportService.SetComponents(mxClient, null, null, null); _server = new StatusWebServer(reportService, _port); _server.Start(); _client = new HttpClient { BaseAddress = new Uri($"http://localhost:{_port}") }; } /// /// Disposes the test HTTP client and stops the status web server. /// public void Dispose() { _client.Dispose(); _server.Dispose(); } /// /// Confirms that the dashboard root responds with HTML content. /// [Fact] public async Task Root_ReturnsHtml200() { var response = await _client.GetAsync("/"); response.StatusCode.ShouldBe(HttpStatusCode.OK); response.Content.Headers.ContentType?.MediaType.ShouldBe("text/html"); } /// /// Confirms that the JSON status endpoint responds successfully. /// [Fact] public async Task ApiStatus_ReturnsJson200() { var response = await _client.GetAsync("/api/status"); response.StatusCode.ShouldBe(HttpStatusCode.OK); response.Content.Headers.ContentType?.MediaType.ShouldBe("application/json"); } /// /// Confirms that the health endpoint returns HTTP 200 when the bridge is healthy. /// [Fact] public async Task ApiHealth_Returns200WhenHealthy() { var response = await _client.GetAsync("/api/health"); // FakeMxAccessClient starts as Connected → healthy response.StatusCode.ShouldBe(HttpStatusCode.OK); var body = await response.Content.ReadAsStringAsync(); body.ShouldContain("healthy"); } /// /// Confirms that unknown dashboard routes return HTTP 404. /// [Fact] public async Task UnknownPath_Returns404() { var response = await _client.GetAsync("/unknown"); response.StatusCode.ShouldBe(HttpStatusCode.NotFound); } /// /// Confirms that unsupported HTTP methods are rejected with HTTP 405. /// [Fact] public async Task PostMethod_Returns405() { var response = await _client.PostAsync("/", new StringContent("")); response.StatusCode.ShouldBe(HttpStatusCode.MethodNotAllowed); } /// /// Confirms that cache-control headers disable caching for dashboard responses. /// [Fact] public async Task CacheHeaders_Present() { var response = await _client.GetAsync("/"); response.Headers.CacheControl?.NoCache.ShouldBe(true); response.Headers.CacheControl?.NoStore.ShouldBe(true); } /// /// Confirms that the server can be started and stopped cleanly. /// [Fact] public void StartStop_DoesNotThrow() { var server2 = new StatusWebServer( new StatusReportService(new HealthCheckService(), 10), new Random().Next(19000, 20000)); server2.Start(); server2.IsRunning.ShouldBe(true); server2.Stop(); } } }