using System.Net;
using System.Text.Json;
using Flyshot.Core.Domain;
using Flyshot.Runtime.Common;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Flyshot.Server.IntegrationTests;
///
/// 使用可控 fake runtime 启动测试宿主,避免集成测试访问真实 FANUC 控制器。
///
public sealed class FlyshotServerFactory : WebApplicationFactory
{
///
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
services.RemoveAll();
services.AddSingleton();
});
}
}
///
/// 为 HTTP 集成测试提供不访问硬件的控制器运行时。
///
internal sealed class IntegrationFakeControllerRuntime : IControllerRuntime
{
private RobotProfile? _robot;
private bool _connected;
private bool _enabled;
private double _speedRatio = 1.0;
private double[] _tcp = [0.0, 0.0, 0.0];
private double[] _joints = Array.Empty();
private double[] _pose = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0];
///
public void ResetRobot(RobotProfile robot, string robotName)
{
_robot = robot;
_connected = false;
_enabled = false;
_joints = new double[robot.DegreesOfFreedom];
}
///
public void Connect(string robotIp)
{
_ = _robot ?? throw new InvalidOperationException("Robot has not been setup.");
_connected = true;
}
///
public void Disconnect()
{
_connected = false;
_enabled = false;
}
///
public void EnableRobot(int bufferSize)
{
if (!_connected)
{
throw new InvalidOperationException("Controller has not been connected.");
}
_enabled = true;
}
///
public void DisableRobot() => _enabled = false;
///
public void StopMove()
{
}
///
public double GetSpeedRatio() => _speedRatio;
///
public void SetSpeedRatio(double ratio) => _speedRatio = ratio;
///
public IReadOnlyList GetTcp() => _tcp.ToArray();
///
public void SetTcp(double x, double y, double z) => _tcp = [x, y, z];
///
public bool GetIo(int port, string ioType) => false;
///
public void SetIo(int port, bool value, string ioType)
{
}
///
public IReadOnlyList GetJointPositions() => _joints.ToArray();
///
public IReadOnlyList GetPose() => _pose.ToArray();
///
public ControllerStateSnapshot GetSnapshot()
{
return new ControllerStateSnapshot(
DateTimeOffset.UtcNow,
_connected ? "Connected" : "Disconnected",
_enabled,
false,
_speedRatio,
_joints,
_pose,
Array.Empty());
}
///
public void ExecuteTrajectory(TrajectoryResult result, IReadOnlyList finalJointPositions)
{
if (!_enabled)
{
throw new InvalidOperationException("Robot has not been enabled.");
}
_joints = finalJointPositions.ToArray();
}
///
public void ExecuteCartesianTrajectory(TrajectoryResult result, IReadOnlyList finalPose)
{
if (!_enabled)
{
throw new InvalidOperationException("Robot has not been enabled.");
}
_pose = [.. finalPose, 1.0];
}
}
public sealed class HealthEndpointTests(FlyshotServerFactory factory) : IClassFixture
{
[Fact]
public async Task GetHealthz_ReturnsOkPayload()
{
using var client = factory.CreateClient();
using var response = await client.GetAsync("/healthz");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
await using var responseStream = await response.Content.ReadAsStreamAsync();
using var jsonDocument = await JsonDocument.ParseAsync(responseStream);
var root = jsonDocument.RootElement;
Assert.Equal("ok", root.GetProperty("status").GetString());
Assert.Equal("flyshot-server-host", root.GetProperty("service").GetString());
}
}