using IvfTl.ControlHost.Debug; using IvfTl.ControlHost.Tests.Fakes; using Newtonsoft.Json.Linq; using System; using Xunit; namespace IvfTl.ControlHost.Tests { public class DebugExecuteTests { private DateTime _now = new DateTime(2026, 6, 23); private (DebugSessionManager mgr, FakeSerial serial, string sid) New() { var serial = new FakeSerial(); var gate = new FakeGate(5, serial); var mgr = new DebugSessionManager(sn => gate, () => _now, 10000, _ => { }); string sid = (string)mgr.Acquire(5).Result; return (mgr, serial, sid); } [Fact] public void Execute_ReadTemp_Dispatches() { var (mgr, serial, sid) = New(); var r = mgr.Execute(sid, "ReadTemp", null); Assert.True(r.Ok); Assert.Contains("Temp", serial.Calls); } [Fact] public void Execute_OpenLed_Dispatches() { var (mgr, serial, sid) = New(); Assert.True(mgr.Execute(sid, "OpenLed", null).Ok); Assert.Contains("OpenLed", serial.Calls); } [Fact] public void Execute_Expired_Session_Rejected() { var (mgr, _, _) = New(); Assert.Equal("SESSION_EXPIRED", mgr.Execute("ghost", "ReadTemp", null).Code); } [Fact] public void Execute_Unknown_Op_Rejected() { var (mgr, _, sid) = New(); var r = mgr.Execute(sid, "NoSuchOp", null); Assert.False(r.Ok); Assert.Equal("BAD_OP", r.Code); } [Fact] public void Vertical_MoveTo_InRange_Dispatches() { var (mgr, serial, sid) = New(); var r = mgr.Execute(sid, "VerticalMoveTo", JObject.FromObject(new { pos = 90000 })); Assert.True(r.Ok); Assert.Contains("VMoveTo(90000)", serial.Calls); } [Fact] public void Vertical_MoveTo_OutOfRange_Rejected_NotDispatched() { var (mgr, serial, sid) = New(); var r = mgr.Execute(sid, "VerticalMoveTo", JObject.FromObject(new { pos = 130000 })); Assert.Equal("OUT_OF_RANGE", r.Code); Assert.DoesNotContain("VMoveTo(130000)", serial.Calls); } [Fact] public void Horizontal_Forward_Relative_Clamped() { var (mgr, serial, sid) = New(); mgr.Execute(sid, "HorizontalMoveTo", JObject.FromObject(new { pos = 215000 })); var r = mgr.Execute(sid, "HorizontalForward", JObject.FromObject(new { value = 10000 })); Assert.Equal("OUT_OF_RANGE", r.Code); } [Fact] public void WriteScanStep_Dispatches() { var (mgr, serial, sid) = New(); Assert.True(mgr.Execute(sid, "WriteScanStep", JObject.FromObject(new { value = 300 })).Ok); Assert.Contains("WriteScanStep(300)", serial.Calls); } [Fact] public void Vertical_Forward_From_RealNonZeroPos_OutOfRange_Rejected() { var (mgr, serial, sid) = New(); serial.VerPos = 120000; // 真机电机已在 120000(未经 MoveTo,跟踪位仍 -1) var r = mgr.Execute(sid, "VerticalForward", JObject.FromObject(new { value = 10000 })); // 目标 130000>125000 Assert.Equal("OUT_OF_RANGE", r.Code); Assert.DoesNotContain("VFwd(10000)", serial.Calls); // 越界绝不下发 } [Fact] public void Vertical_Forward_From_RealPos_InRange_Dispatches_And_TracksRealPos() { var (mgr, serial, sid) = New(); serial.VerPos = 90000; var r = mgr.Execute(sid, "VerticalForward", JObject.FromObject(new { value = 10000 })); // 目标 100000 OK Assert.True(r.Ok); Assert.Contains("VFwd(10000)", serial.Calls); } } }