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

namespace XUnitTest.Services;

/// <summary>会话管理器测试</summary>
[TestCaseOrderer("NewLife.UnitTest.DefaultOrderer", "NewLife.UnitTest")]
public class SessionManagerTests
{
    #region ICommandSession测试
    [Fact(DisplayName = "命令会话基本属性")]
    public void CommandSessionBasicProperties()
    {
        var session = new CommandSession
        {
            Code = "device001"
        };

        Assert.Equal("device001", session.Code);
        Assert.True(session.Active);
    }

    [Fact(DisplayName = "命令会话处理")]
    public async Task CommandSessionHandleAsync()
    {
        var session = new CommandSession
        {
            Code = "device001"
        };

        var cmd = new CommandModel
        {
            Id = 1,
            Command = "test"
        };

        // 基类HandleAsync默认返回已完成任务
        await session.HandleAsync(cmd, null, CancellationToken.None);
        Assert.True(true);
    }
    #endregion

    #region SessionManager测试
    [Fact(DisplayName = "会话管理器添加和获取")]
    public void SessionManagerAddAndGet()
    {
        var services = new ServiceCollection();
        services.AddSingleton<ICacheProvider>(new MemoryCacheProvider());
        var sp = services.BuildServiceProvider();

        using var manager = new SessionManager(sp);

        var session = new CommandSession { Code = "device001" };
        manager.Add(session);

        var result = manager.Get("device001");
        Assert.NotNull(result);
        Assert.Same(session, result);
    }

    [Fact(DisplayName = "会话管理器删除")]
    public void SessionManagerRemove()
    {
        var services = new ServiceCollection();
        services.AddSingleton<ICacheProvider>(new MemoryCacheProvider());
        var sp = services.BuildServiceProvider();

        using var manager = new SessionManager(sp);

        var session = new CommandSession { Code = "device002" };
        manager.Add(session);

        Assert.NotNull(manager.Get("device002"));

        manager.Remove(session);

        Assert.Null(manager.Get("device002"));
    }

    [Fact(DisplayName = "会话管理器不存在的会话")]
    public void SessionManagerGetNotExist()
    {
        var services = new ServiceCollection();
        services.AddSingleton<ICacheProvider>(new MemoryCacheProvider());
        var sp = services.BuildServiceProvider();

        using var manager = new SessionManager(sp);

        var result = manager.Get("notexist");
        Assert.Null(result);
    }

    [Fact(DisplayName = "会话管理器发布消息")]
    public async Task SessionManagerPublishAsync()
    {
        var services = new ServiceCollection();
        services.AddSingleton<ICacheProvider>(new MemoryCacheProvider());
        var sp = services.BuildServiceProvider();

        using var manager = new SessionManager(sp);

        var session = new TestCommandSession { Code = "device003" };
        manager.Add(session);

        var cmd = new CommandModel
        {
            Id = 1,
            Command = "test",
            Argument = "arg1"
        };

        await manager.PublishAsync("device003", cmd, null, CancellationToken.None);

        // 等待消息处理
        await Task.Delay(100);

        Assert.True(session.Handled);
        Assert.Equal("test", session.LastCommand?.Command);
    }

    [Fact(DisplayName = "会话管理器空命令检查")]
    public async Task SessionManagerPublishNullCommand()
    {
        var services = new ServiceCollection();
        services.AddSingleton<ICacheProvider>(new MemoryCacheProvider());
        var sp = services.BuildServiceProvider();

        using var manager = new SessionManager(sp);

        await Assert.ThrowsAsync<ArgumentNullException>(() =>
            manager.PublishAsync("device001", null!, null, CancellationToken.None));
    }
    #endregion

    #region ITokenService Mock测试
    [Fact(DisplayName = "令牌服务Mock测试")]
    public void TokenServiceMock()
    {
        var mockTokenService = new Mock<ITokenService>();
        mockTokenService.Setup(x => x.IssueToken(It.IsAny<String>(), It.IsAny<String>()))
            .Returns(new TokenModel
            {
                AccessToken = "test_token",
                TokenType = "JWT",
                ExpireIn = 3600
            });

        var token = mockTokenService.Object.IssueToken("device001", "client001");

        Assert.Equal("test_token", token.AccessToken);
        Assert.Equal("JWT", (token as TokenModel)?.TokenType);
        Assert.Equal(3600, token.ExpireIn);
    }
    #endregion

    #region 辅助类
    /// <summary>测试命令会话</summary>
    private class TestCommandSession : CommandSession
    {
        public Boolean Handled { get; private set; }
        public CommandModel? LastCommand { get; private set; }

        public override Task HandleAsync(CommandModel command, String? message, CancellationToken cancellationToken)
        {
            Handled = true;
            LastCommand = command;
            return Task.CompletedTask;
        }
    }

    /// <summary>内存缓存提供者(简化实现用于测试)</summary>
    private class MemoryCacheProvider : ICacheProvider
    {
        private readonly ICache _cache = new MemoryCache();

        public ICache Cache { get => _cache; set { } }

        public ICache InnerCache { get => _cache; set { } }

        public IProducerConsumer<T> GetQueue<T>(String topic) => _cache.GetQueue<T>(topic);

        public IProducerConsumer<T> GetQueue<T>(String topic, String? group) => _cache.GetQueue<T>(topic);

        public IProducerConsumer<T> GetInnerQueue<T>(String topic) => _cache.GetQueue<T>(topic);

        public IProducerConsumer<T> GetStack<T>(String topic) => _cache.GetStack<T>(topic);

        public IDisposable AcquireLock(String key, Int32 msTimeout) => throw new NotImplementedException();
    }
    #endregion
}