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
{
///
/// operate 端 DebugSessionClient 纯逻辑单测:用 fake HttpMessageHandler 注入响应,
/// 验证 acquire 解析(含培养态)/SESSION_EXPIRED 触发失效回调/release 幂等/心跳定时器。
///
public class DebugSessionClientTests
{
private sealed class FakeHandler : HttpMessageHandler
{
public Func Responder;
public int HeartbeatCount;
protected override Task 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(180);
await c.ReleaseAsync();
Assert.True(h.HeartbeatCount >= 2); // 50ms 间隔 180ms 内至少 2 次
}
}
}