using System; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using ivf_tl_Operate.Debug; using Newtonsoft.Json.Linq; using Xunit; namespace IvfTl.ControlHost.Tests { /// /// operate 端 CalibrationClient 纯逻辑单测:用 fake HttpMessageHandler 注入 control 端 /// /debug/calibrate/* 的响应,验证四个端点的路径/请求体/解析。 /// 进度响应是 control 端 JsonConvert.SerializeObject(DebugCommandResult)——result 内嵌 /// CalibProgress(PascalCase 字段,WellCalibState 枚举默认序列化为数字)。 /// public class CalibrationClientTests { private sealed class FakeHandler : HttpMessageHandler { public Func Responder; public string LastPath; public string LastBody; protected override async Task SendAsync(HttpRequestMessage req, CancellationToken ct) { LastPath = req.RequestUri.AbsolutePath; LastBody = req.Content != null ? await req.Content.ReadAsStringAsync() : ""; var (code, body) = Responder(req, LastBody); return new HttpResponseMessage(code) { Content = new StringContent(body) }; } } private const string Sid = "sid123"; [Fact] public async Task StartAsync_PostsToStart_WithSessionIdAndWells_ParsesOk() { var h = new FakeHandler { Responder = (_, __) => (HttpStatusCode.OK, "{\"ok\":true}") }; var c = new CalibrationClient("http://127.0.0.1:9/", Sid, new HttpClient(h)); var r = await c.StartAsync(new[] { 1, 2, 3 }); Assert.True(r.Ok); Assert.Equal("/debug/calibrate/start", h.LastPath); var jo = JObject.Parse(h.LastBody); Assert.Equal(Sid, jo["sessionId"]?.ToString()); Assert.Equal(new[] { 1, 2, 3 }, ((JArray)jo["wells"]).ToObject()); } [Fact] public async Task PollProgressAsync_ParsesCalibProgress_TotalCompletedIsRunning_AndWells() { // control 端真实形态:DebugCommandResult{ok, result=CalibProgress}(PascalCase,枚举=数字)。 string body = "{\"ok\":true,\"result\":{" + "\"Total\":16,\"Completed\":2,\"CurrentWell\":3,\"IsRunning\":true,\"Wells\":[" + "{\"WellSn\":1,\"State\":2,\"FocusZ\":90000,\"Exposure\":120,\"PeakRatio\":1.8,\"CenterOffsetPct\":0.5,\"CircleFound\":true,\"Note\":\"\"}," + "{\"WellSn\":2,\"State\":3,\"FocusZ\":0,\"Exposure\":0,\"PeakRatio\":1.0,\"CenterOffsetPct\":0.0,\"CircleFound\":false,\"Note\":\"伪峰\"}" + "]}}"; var h = new FakeHandler { Responder = (_, __) => (HttpStatusCode.OK, body) }; var c = new CalibrationClient("http://127.0.0.1:9/", Sid, new HttpClient(h)); var p = await c.PollProgressAsync(); Assert.Equal("/debug/calibrate/progress", h.LastPath); Assert.Equal(Sid, JObject.Parse(h.LastBody)["sessionId"]?.ToString()); Assert.NotNull(p); Assert.Equal(16, p.Total); Assert.Equal(2, p.Completed); Assert.Equal(3, p.CurrentWell); Assert.True(p.IsRunning); Assert.Equal(2, p.Wells.Count); Assert.Equal(1, p.Wells[0].WellSn); Assert.Equal(2, p.Wells[0].State); // Qualified=2 Assert.Equal(90000, p.Wells[0].FocusZ); Assert.True(p.Wells[0].CircleFound); Assert.Equal(1.8, p.Wells[0].PeakRatio, 3); Assert.Equal(3, p.Wells[1].State); // FakePeak=3 Assert.Equal("伪峰", p.Wells[1].Note); } [Fact] public async Task RecalibrateAsync_PostsToRecalibrate_WithWellSn() { var h = new FakeHandler { Responder = (_, __) => (HttpStatusCode.OK, "{\"ok\":true,\"result\":true}") }; var c = new CalibrationClient("http://127.0.0.1:9/", Sid, new HttpClient(h)); var r = await c.RecalibrateAsync(7); Assert.True(r.Ok); Assert.Equal("/debug/calibrate/recalibrate", h.LastPath); var jo = JObject.Parse(h.LastBody); Assert.Equal(Sid, jo["sessionId"]?.ToString()); Assert.Equal(7, (int)jo["wellSn"]); } [Fact] public async Task StopAsync_PostsToStop_WithSessionId() { var h = new FakeHandler { Responder = (_, __) => (HttpStatusCode.OK, "{\"ok\":true}") }; var c = new CalibrationClient("http://127.0.0.1:9/", Sid, new HttpClient(h)); var r = await c.StopAsync(); Assert.True(r.Ok); Assert.Equal("/debug/calibrate/stop", h.LastPath); Assert.Equal(Sid, JObject.Parse(h.LastBody)["sessionId"]?.ToString()); } [Fact] public async Task SessionExpired_CodeIsReadable() { var h = new FakeHandler { Responder = (_, __) => (HttpStatusCode.Gone, "{\"ok\":false,\"code\":\"SESSION_EXPIRED\",\"error\":\"会话不存在或已过期\"}") }; var c = new CalibrationClient("http://127.0.0.1:9/", Sid, new HttpClient(h)); var r = await c.StartAsync(); Assert.False(r.Ok); Assert.Equal("SESSION_EXPIRED", r.Code); } } }