| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182 |
- using System;
- using System.Net;
- using System.Net.Http;
- using System.Threading;
- using System.Threading.Tasks;
- using ivf_tl_Operate.Debug;
- using Xunit;
- namespace IvfTl.ControlHost.Tests
- {
- /// <summary>
- /// operate 端 DebugSessionClient 纯逻辑单测:用 fake HttpMessageHandler 注入响应,
- /// 验证 acquire 解析(含培养态)/SESSION_EXPIRED 触发失效回调/release 幂等/心跳定时器。
- /// </summary>
- public class DebugSessionClientTests
- {
- private sealed class FakeHandler : HttpMessageHandler
- {
- public Func<HttpRequestMessage, (HttpStatusCode, string)> Responder;
- public int HeartbeatCount;
- protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage req, CancellationToken ct)
- {
- if (req.RequestUri.AbsolutePath.Contains("heartbeat")) Interlocked.Increment(ref HeartbeatCount);
- var (code, body) = Responder(req);
- return Task.FromResult(new HttpResponseMessage(code) { Content = new StringContent(body) });
- }
- }
- [Fact]
- public async Task Acquire_ParsesSessionIdAndCultivation()
- {
- var h = new FakeHandler { Responder = _ => (HttpStatusCode.OK, "{\"ok\":true,\"result\":\"sid123\",\"cultivating\":true,\"embryoCount\":3}") };
- var c = new DebugSessionClient("http://127.0.0.1:9/", new HttpClient(h));
- var r = await c.AcquireAsync(6);
- Assert.True(r.Ok);
- Assert.Equal("sid123", r.SessionId);
- Assert.True(r.Cultivating);
- Assert.Equal(3, r.EmbryoCount);
- await c.ReleaseAsync();
- }
- [Fact]
- public async Task Command_SessionExpired_FiresOnSessionExpired()
- {
- bool fired = false;
- var h = new FakeHandler
- {
- Responder = req => req.RequestUri.AbsolutePath.Contains("command")
- ? (HttpStatusCode.Gone, "{\"ok\":false,\"code\":\"SESSION_EXPIRED\",\"error\":\"过期\"}")
- : (HttpStatusCode.OK, "{\"ok\":true,\"result\":\"sid123\"}")
- };
- var c = new DebugSessionClient("http://127.0.0.1:9/", new HttpClient(h));
- c.OnSessionExpired = () => fired = true;
- await c.AcquireAsync(6);
- var r = await c.CommandAsync("ReadTemp", null);
- Assert.False(r.Ok);
- Assert.Equal("SESSION_EXPIRED", r.Code);
- Assert.True(fired);
- }
- [Fact]
- public async Task Release_IsIdempotent_NoThrowWhenCalledTwice()
- {
- var h = new FakeHandler { Responder = _ => (HttpStatusCode.OK, "{\"ok\":true,\"result\":\"sid123\"}") };
- var c = new DebugSessionClient("http://127.0.0.1:9/", new HttpClient(h));
- await c.AcquireAsync(6);
- await c.ReleaseAsync();
- await c.ReleaseAsync(); // 第二次不抛
- }
- [Fact]
- public async Task Heartbeat_TimerSendsAfterAcquire()
- {
- var h = new FakeHandler { Responder = _ => (HttpStatusCode.OK, "{\"ok\":true,\"result\":\"sid123\"}") };
- var c = new DebugSessionClient("http://127.0.0.1:9/", new HttpClient(h)) { HeartbeatIntervalMs = 50 };
- await c.AcquireAsync(6);
- await Task.Delay(300);
- await c.ReleaseAsync();
- Assert.True(h.HeartbeatCount >= 2); // 50ms 间隔 300ms 内至少 2 次(放宽窗口防 CI 慢机 flaky)
- }
- }
- }
|