节点在线、应用在线、配置在线使用令牌查询
大石头 authored at 2021-12-16 19:49:30
4.93 KiB
Stardust
using System.Text.Json;
using Stardust.Data.Monitors;
using Stardust.WebApiTests.Fixtures;
using Xunit;
using Xunit.Abstractions;

namespace Stardust.WebApiTests.Security;

/// <summary>
/// task-95 二期刀3/刀4(2026-11-12,盘点稿 Doc/安全-02 §4 刀3/4)HTTP 集成式迁移(2026-11-14 Q2 裁决):
/// AlarmGroup/AppTracer WebHook 行级掩码+F2 消解。刀3/4 偏离一期「值级 null」惯例(刀6 "••••••" 同款哨兵):
/// F2=前端编辑抽屉从列表行回填且 PUT 恒带 webHook 键(alarmGroup.logic groupBody / appTracer.logic tracerPutBody)
/// ——null 回填→PUT 空→静默清库;哨兵回填→原样提交→Valid POST 守卫识别还原。断言=键在值等于哨兵(非 null)。
/// 与原 ServerTests 反射版差异:Valid GET 回显(MVC 编辑表单输入侧)非 API 面,不再断言;
/// 空值放行(显式清空)改经真实 PUT 管道;读回走实体 Find 直连查询。
/// </summary>
[Collection(WebApiCollection.Name)]
public class MaskedRowsKnife34ApiTests
{
    private const String Mask = "••••••";

    private readonly ApiClient _api;

    public MaskedRowsKnife34ApiTests(WebApiTestFactory factory, ITestOutputHelper output) =>
        _api = new(factory, output);

    [Fact(DisplayName = "task-95 刀3:AlarmGroup 列表行 WebHook=掩码哨兵(F2 哨兵语义,非 null)")]
    public async Task AlarmGroup_Index_WebHook_Sentinel()
    {
        var tag = "mw3a-" + Guid.NewGuid().ToString("N")[..8];
        var e = new AlarmGroup
        {
            Name = tag + "-grp", WebHook = "https://hook.example/" + tag, Enable = true,
        };
        e.Insert();
        try
        {
            var rows = await ApiRowAssert.GetRowsAsync(_api, "Monitors/AlarmGroup?pageIndex=0&pageSize=200");
            var mine = ApiRowAssert.Mine(rows, "name", tag);
            Assert.True(mine.Count >= 1, $"应能查到造数行 {tag}*,实际行数={rows.Count}");

            ApiRowAssert.Sentinel(mine, "webHook", Mask, "AlarmGroup");
        }
        finally { e.Delete(); }
    }

    [Fact(DisplayName = "task-95 刀3:AlarmGroup Detail 哨兵;PUT 守卫真写读回原值;空串放行=显式清空")]
    public async Task AlarmGroup_Detail_Guard_And_Clear()
    {
        var tag = "mw3b-" + Guid.NewGuid().ToString("N")[..8];
        var e = new AlarmGroup
        {
            Name = tag + "-grp", WebHook = "https://keep.example/" + tag, Enable = true,
        };
        e.Insert();
        try
        {
            // Detail 掩码哨兵
            var detail = await _api.GetJsonAsync($"Monitors/AlarmGroup/{e.Id}");
            ApiRowAssert.DetailSentinel(detail.Data, "webHook", Mask, "AlarmGroup");

            // PUT 守卫:哨兵原样提交(F2 回填形态)→ Valid 识别还原 → 真写读回原值
            var put = new { id = e.Id, name = e.Name, webHook = Mask, enable = false };
            var api = await _api.PutApiAsync<JsonElement>($"Monitors/AlarmGroup/{e.Id}", put);
            var back = AlarmGroup.FindById(e.Id);
            Assert.NotNull(back);
            Assert.Equal("https://keep.example/" + tag, back.WebHook);

            // 空值放行:显式清空(空串不走哨兵分支直通写库)
            var clear = new { id = e.Id, name = e.Name, webHook = "", enable = true };
            var api2 = await _api.PutApiAsync<JsonElement>($"Monitors/AlarmGroup/{e.Id}", clear);
            var cleared = AlarmGroup.FindById(e.Id);
            Assert.NotNull(cleared);
            Assert.True(String.IsNullOrEmpty(cleared.WebHook),
                $"空串 PUT 应显式清空 WebHook,实际='{cleared.WebHook}'");
        }
        finally { e.Delete(); }
    }

    [Fact(DisplayName = "task-95 刀4:AppTracer 列表行/Detail WebHook=掩码哨兵 + PUT 守卫真写读回")]
    public async Task AppTracer_Row_Detail_Guard()
    {
        var tag = "mw4-" + Guid.NewGuid().ToString("N")[..8];
        var e = new AppTracer
        {
            Name = tag + "-trc", WebHook = "https://tracer.example/" + tag, Enable = true,
        };
        e.Insert();
        try
        {
            var rows = await ApiRowAssert.GetRowsAsync(_api, "Monitors/AppTracer?pageIndex=0&pageSize=200");
            var mine = ApiRowAssert.Mine(rows, "name", tag);
            Assert.True(mine.Count >= 1, $"应能查到造数行 {tag}*,实际行数={rows.Count}");
            ApiRowAssert.Sentinel(mine, "webHook", Mask, "AppTracer");

            var detail = await _api.GetJsonAsync($"Monitors/AppTracer/{e.ID}");
            ApiRowAssert.DetailSentinel(detail.Data, "webHook", Mask, "AppTracer");

            var put = new { id = e.ID, webHook = Mask };
            var api = await _api.PutApiAsync<JsonElement>($"Monitors/AppTracer/{e.ID}", put);
            var back = AppTracer.FindByKey(e.ID);
            Assert.NotNull(back);
            Assert.Equal("https://tracer.example/" + tag, back.WebHook);
        }
        finally { e.Delete(); }
    }
}