节点在线、应用在线、配置在线使用令牌查询
大石头 authored at 2021-12-16 19:49:30
4.24 KiB
Stardust
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Stardust.Data.Platform;
using Stardust.WebApiTests.Fixtures;
using Xunit;

namespace Stardust.WebApiTests.Mcp;

/// <summary>
/// G1 修复回归测试:McpTokenController.OnUpdate 在 JSON/API 请求(无表单内容类型)下
/// 不得触发 SyncByToken 清空该令牌的资源授权。
/// 背景:OnInsert 有 resources.Count>0 守卫而 OnUpdate 没有;前端 updateToken/setTokenEnable
/// 发 JSON PUT(不含 res_* 字段),ParseResourcesFromForm 返回空列表,SyncByToken 先删后插
/// 导致该令牌全部资源授权被清空(McpGuard 对零授权放行,反而扩大权限)。
/// </summary>
[Collection(WebApiCollection.Name)]
public class McpTokenUpdateGuardTests
{
    [Fact(DisplayName = "G1: OnUpdate 在 JSON 请求下不清空资源授权")]
    public void OnUpdate_JsonRequest_KeepsResources()
    {
        // 建令牌(不手动指定 Id,避免 XCode 自增忽略)
        var token = new McpToken
        {
            Name = "g1-guard-test",
            Enable = true,
            Token = "sdmcp_test_guard_" + Guid.NewGuid().ToString("N")[..8],
            ProjectId = 1,
        };

        var resource = default(McpTokenResource);
        try
        {
            Assert.True(token.Insert() > 0);
            var tokenId = token.Id;
            Assert.True(tokenId > 0);

            // 建一条资源授权(模拟已有白名单)
            resource = new McpTokenResource
            {
                TokenId = tokenId,
                ResourceType = McpResourceType.Project.ToStorageName(),
                ResourceId = 1,
                IsAll = false,
                Enable = true,
            };
            Assert.True(resource.Insert() > 0);

            // 反射构造 WebApi 宿主控制器(程序集 StarWebApi,与 Web 宿主同命名空间)
            var type = Type.GetType("Stardust.Web.Areas.Platform.Controllers.McpTokenController, StarWebApi");
            Assert.NotNull(type);

            var controller = Activator.CreateInstance(type!)!;
            // 无表单内容类型的请求上下文(模拟前端 JSON PUT)
            var httpContext = new DefaultHttpContext();
            typeof(ControllerBase).GetProperty(nameof(ControllerBase.ControllerContext))!
                .SetValue(controller, new ControllerContext { HttpContext = httpContext });

            var method = type!.GetMethod("OnUpdate", BindingFlags.Instance | BindingFlags.NonPublic);
            Assert.NotNull(method);

            var entity = McpToken.FindById(tokenId);
            Assert.NotNull(entity);
            entity!.Name = "g1-guard-test-renamed";

            // 执行 OnUpdate(JSON 请求:Request.HasFormContentType == false)
            method!.Invoke(controller, new[] { entity });

            // 资源授权必须仍在
            var kept = McpTokenResource.FindAllByTokenId(tokenId);
            Assert.NotNull(kept);
            Assert.Single(kept!);
            Assert.Equal(McpResourceType.Project.ToStorageName(), kept![0].ResourceType);
            Assert.Equal(1, kept[0].ResourceId);
        }
        finally
        {
            if (resource != null) resource.Delete();
            token.Delete();
        }
    }

    [Fact(DisplayName = "G1: ParseResourcesFromForm 在 JSON 请求下返回空列表(不抛异常)")]
    public void ParseResourcesFromForm_JsonRequest_ReturnsEmpty()
    {
        var type = Type.GetType("Stardust.Web.Areas.Platform.Controllers.McpTokenController, StarWebApi");
        Assert.NotNull(type);

        var controller = Activator.CreateInstance(type!)!;
        var httpContext = new DefaultHttpContext();
        typeof(ControllerBase).GetProperty(nameof(ControllerBase.ControllerContext))!
            .SetValue(controller, new ControllerContext { HttpContext = httpContext });

        var method = type!.GetMethod("ParseResourcesFromForm", BindingFlags.Instance | BindingFlags.NonPublic);
        Assert.NotNull(method);

        var result = method!.Invoke(controller, null);
        Assert.NotNull(result);
        var list = Assert.IsAssignableFrom<IList<McpTokenResource>>(result);
        Assert.Empty(list);
    }
}