using System.Net; using System.Text; using ScadaLink.CLI; namespace ScadaLink.CLI.Tests; /// /// Regression tests for CLI-013 — /// (success, error-body parsing, connection-failure, and timeout paths) was untested. /// Uses a stub so no live server is required. /// public class ManagementHttpClientTests { private sealed class StubHandler : HttpMessageHandler { private readonly Func> _responder; public StubHandler(HttpStatusCode status, string body) : this((_, _) => Task.FromResult(new HttpResponseMessage(status) { Content = new StringContent(body, Encoding.UTF8, "application/json"), })) { } public StubHandler(Func> responder) { _responder = responder; } protected override Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) => _responder(request, cancellationToken); } private static ManagementHttpClient ClientWith(StubHandler handler) => new(new HttpClient(handler), "http://localhost:9001", "user", "pass"); [Fact] public async Task SendCommandAsync_Success_ReturnsJsonData() { using var client = ClientWith(new StubHandler(HttpStatusCode.OK, "{\"id\":1}")); var response = await client.SendCommandAsync("ListSites", new { }, TimeSpan.FromSeconds(5)); Assert.Equal(200, response.StatusCode); Assert.Equal("{\"id\":1}", response.JsonData); Assert.Null(response.Error); Assert.Null(response.ErrorCode); } [Fact] public async Task SendCommandAsync_ErrorBody_ParsesErrorAndCode() { using var client = ClientWith(new StubHandler( HttpStatusCode.BadRequest, "{\"error\":\"Bad input\",\"code\":\"INVALID_ARGUMENT\"}")); var response = await client.SendCommandAsync("ListSites", new { }, TimeSpan.FromSeconds(5)); Assert.Equal(400, response.StatusCode); Assert.Null(response.JsonData); Assert.Equal("Bad input", response.Error); Assert.Equal("INVALID_ARGUMENT", response.ErrorCode); } [Fact] public async Task SendCommandAsync_NonJsonErrorBody_FallsBackToRawBody() { using var client = ClientWith(new StubHandler( HttpStatusCode.BadGateway, "Bad Gateway")); var response = await client.SendCommandAsync("ListSites", new { }, TimeSpan.FromSeconds(5)); Assert.Equal(502, response.StatusCode); Assert.Equal("Bad Gateway", response.Error); Assert.Null(response.ErrorCode); } [Fact] public async Task SendCommandAsync_ConnectionFailure_ReturnsStatusZero() { using var client = ClientWith(new StubHandler((_, _) => throw new HttpRequestException("connection refused"))); var response = await client.SendCommandAsync("ListSites", new { }, TimeSpan.FromSeconds(5)); Assert.Equal(0, response.StatusCode); Assert.Equal("CONNECTION_FAILED", response.ErrorCode); Assert.Contains("connection refused", response.Error); } [Fact] public async Task SendCommandAsync_Timeout_Returns504() { using var client = ClientWith(new StubHandler(async (_, ct) => { await Task.Delay(Timeout.Infinite, ct); return new HttpResponseMessage(HttpStatusCode.OK); })); var response = await client.SendCommandAsync("ListSites", new { }, TimeSpan.FromMilliseconds(50)); Assert.Equal(504, response.StatusCode); Assert.Equal("TIMEOUT", response.ErrorCode); } }