|
@@ -0,0 +1,114 @@
|
|
|
|
|
+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
|
|
|
|
|
+{
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// operate 端 CalibrationClient 纯逻辑单测:用 fake HttpMessageHandler 注入 control 端
|
|
|
|
|
+ /// /debug/calibrate/* 的响应,验证四个端点的路径/请求体/解析。
|
|
|
|
|
+ /// 进度响应是 control 端 JsonConvert.SerializeObject(DebugCommandResult)——result 内嵌
|
|
|
|
|
+ /// CalibProgress(PascalCase 字段,WellCalibState 枚举默认序列化为数字)。
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public class CalibrationClientTests
|
|
|
|
|
+ {
|
|
|
|
|
+ private sealed class FakeHandler : HttpMessageHandler
|
|
|
|
|
+ {
|
|
|
|
|
+ public Func<HttpRequestMessage, string, (HttpStatusCode, string)> Responder;
|
|
|
|
|
+ public string LastPath;
|
|
|
|
|
+ public string LastBody;
|
|
|
|
|
+ protected override async Task<HttpResponseMessage> 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<int[]>());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [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);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|