RPC远程过程调用,二进制封装,提供高吞吐低延迟的高性能RPC框架
大石头 authored at 2022-08-10 13:26:19
7.62 KiB
NewLife.Remoting
using System;
using System.Linq;
using System.Threading.Tasks;
using NewLife;
using NewLife.Data;
using NewLife.Log;
using NewLife.Remoting;
using Xunit;

namespace XUnitTest.Remoting;

/// <summary>多控制器路由与高级调用集成测试</summary>
/// <remarks>
/// 验证 ApiServer 注册多个控制器时的命名空间路由、
/// 异步方法返回 Task 的处理、IApi 接口注入 Session 等行为。
/// </remarks>
public class MultiControllerRoutingTests : DisposeBase
{
    private readonly ApiServer _Server;
    private readonly Int32 _Port;

    public MultiControllerRoutingTests()
    {
        _Server = new ApiServer(0)
        {
            Log = XTrace.Log,
            ShowError = true,
        };
        _Server.Register<UserController>();
        _Server.Register<OrderController>();
        _Server.Register<AsyncController>();
        _Server.Start();

        _Port = _Server.Port;
    }

    protected override void Dispose(Boolean disposing)
    {
        base.Dispose(disposing);
        _Server.TryDispose();
    }

    #region 多控制器命名空间路由
    [Fact(DisplayName = "多控制器_按命名空间路由")]
    public async Task MultiControllerNamespaceRoutingTest()
    {
        using var client = new ApiClient($"tcp://127.0.0.1:{_Port}");

        // User 控制器
        var userName = await client.InvokeAsync<String>("User/GetName", new { id = 1 });
        Assert.Equal("User_1", userName);

        // Order 控制器
        var orderInfo = await client.InvokeAsync<String>("Order/GetOrder", new { orderId = 100 });
        Assert.Equal("Order_100", orderInfo);
    }

    [Fact(DisplayName = "多控制器_API列表包含所有服务")]
    public async Task MultiControllerApiListTest()
    {
        using var client = new ApiClient($"tcp://127.0.0.1:{_Port}");

        var apis = await client.InvokeAsync<String[]>("Api/All");
        Assert.NotNull(apis);

        // 应包含 User 和 Order 控制器的方法
        Assert.Contains(apis, a => a.Contains("User/GetName"));
        Assert.Contains(apis, a => a.Contains("Order/GetOrder"));
        Assert.Contains(apis, a => a.Contains("Async/GetValue"));
    }

    [Fact(DisplayName = "多控制器_调用不存在的控制器")]
    public async Task NonExistentControllerTest()
    {
        using var client = new ApiClient($"tcp://127.0.0.1:{_Port}");

        var ex = await Assert.ThrowsAsync<ApiException>(
            () => client.InvokeAsync<Object>("NonExistent/Action"));

        Assert.Equal(404, ex.Code);
    }

    [Fact(DisplayName = "多控制器_调用不存在的方法")]
    public async Task NonExistentMethodTest()
    {
        using var client = new ApiClient($"tcp://127.0.0.1:{_Port}");

        var ex = await Assert.ThrowsAsync<ApiException>(
            () => client.InvokeAsync<Object>("User/NonExistentMethod"));

        Assert.Equal(404, ex.Code);
    }
    #endregion

    #region 异步控制器方法
    [Fact(DisplayName = "异步控制器_Task返回值")]
    public async Task AsyncControllerTaskReturnTest()
    {
        using var client = new ApiClient($"tcp://127.0.0.1:{_Port}");

        var result = await client.InvokeAsync<Int32>("Async/GetValue");
        Assert.Equal(42, result);
    }

    [Fact(DisplayName = "异步控制器_TaskString返回值")]
    public async Task AsyncControllerTaskStringReturnTest()
    {
        using var client = new ApiClient($"tcp://127.0.0.1:{_Port}");

        var result = await client.InvokeAsync<String>("Async/GetMessage", new { name = "World" });
        Assert.Equal("Hello, World!", result);
    }

    [Fact(DisplayName = "异步控制器_Task异常传播")]
    public async Task AsyncControllerExceptionTest()
    {
        using var client = new ApiClient($"tcp://127.0.0.1:{_Port}");

        var ex = await Assert.ThrowsAsync<ApiException>(
            () => client.InvokeAsync<Object>("Async/ThrowAsync"));

        Assert.Equal(500, ex.Code);
    }
    #endregion

    #region IApi接口Session注入
    [Fact(DisplayName = "IApi接口_Session注入")]
    public async Task IApiSessionInjectionTest()
    {
        using var client = new ApiClient($"tcp://127.0.0.1:{_Port}");

        var result = await client.InvokeAsync<Boolean>("User/HasSession");
        Assert.True(result);
    }
    #endregion

    #region 注册实例 vs 注册类型
    [Fact(DisplayName = "注册实例_共享控制器状态")]
    public async Task RegisterInstanceSharedStateTest()
    {
        using var server = new ApiServer(0) { Log = XTrace.Log, ShowError = true };

        var counter = new CounterController();
        server.Register(counter, null);
        server.Start();

        using var client = new ApiClient($"tcp://127.0.0.1:{server.Port}");

        // 实例注册共享状态
        var r1 = await client.InvokeAsync<Int32>("Counter/Increment");
        var r2 = await client.InvokeAsync<Int32>("Counter/Increment");
        var r3 = await client.InvokeAsync<Int32>("Counter/Increment");

        Assert.Equal(1, r1);
        Assert.Equal(2, r2);
        Assert.Equal(3, r3);
    }
    #endregion

    #region IPacket参数和返回值
    [Fact(DisplayName = "IPacket_二进制参数和返回")]
    public async Task PacketParameterAndReturnTest()
    {
        using var server = new ApiServer(0) { Log = XTrace.Log, ShowError = true };
        server.Register<PacketController>();
        server.Start();

        using var client = new ApiClient($"tcp://127.0.0.1:{server.Port}");

        var data = new Byte[256];
        for (var i = 0; i < data.Length; i++) data[i] = (Byte)(i & 0xFF);

        var result = await client.InvokeAsync<Packet>("Packet/Echo", data);
        Assert.NotNull(result);
        Assert.Equal(data.Length, result.Total);
        Assert.True(data.SequenceEqual(result.ToArray()));
    }

    [Fact(DisplayName = "IPacket_XOR变换")]
    public async Task PacketTransformTest()
    {
        using var server = new ApiServer(0) { Log = XTrace.Log, ShowError = true };
        server.Register<PacketController>();
        server.Start();

        using var client = new ApiClient($"tcp://127.0.0.1:{server.Port}");

        var data = new Byte[128];
        Array.Fill(data, (Byte)0xAA);

        var result = await client.InvokeAsync<Packet>("Packet/Xor", data);
        Assert.NotNull(result);
        Assert.Equal(data.Length, result.Total);

        var expected = data.Select(b => (Byte)(b ^ 0xFF)).ToArray();
        Assert.True(expected.SequenceEqual(result.ToArray()));
    }
    #endregion

    #region 辅助类
    class UserController : IApi
    {
        public IApiSession? Session { get; set; }

        public String GetName(Int32 id) => $"User_{id}";

        public Boolean HasSession() => Session != null;
    }

    class OrderController
    {
        public String GetOrder(Int32 orderId) => $"Order_{orderId}";
    }

    class AsyncController
    {
        public async Task<Int32> GetValue()
        {
            await Task.Delay(10);
            return 42;
        }

        public async Task<String> GetMessage(String name)
        {
            await Task.Delay(10);
            return $"Hello, {name}!";
        }

        public async Task<String> ThrowAsync()
        {
            await Task.Delay(10);
            throw new InvalidOperationException("异步异常");
        }
    }

    class CounterController
    {
        private Int32 _count;

        public Int32 Increment() => ++_count;
    }

    class PacketController
    {
        public IPacket Echo(IPacket pk) => pk;

        public IPacket Xor(IPacket pk)
        {
            var buf = pk.ReadBytes();
            for (var i = 0; i < buf.Length; i++) buf[i] ^= 0xFF;
            return (ArrayPacket)buf;
        }
    }
    #endregion
}