using System.Reflection;
using Microsoft.AspNetCore.Authorization;
using Admin.NET.Plugin.AiDOP.MaterialWarehouse;
using Xunit;
namespace Admin.NET.Plugin.AiDOP.Tests.S5.MaterialWarehouse;
///
/// FUNC-S5-012 盘点标签确认:匿名访问收口守卫。
///
/// 纯反射断言,不连库、不起服务,普通 CI 也会执行(无 AIDOP_IT 门禁)。
/// 背景:该服务原先类级带 [AllowAnonymous],实测匿名 GET(无 Authorization 头、
/// credentials:'omit')返回 HTTP 200。本组用例把「不得再标注匿名」固化下来。
///
///
/// 注意:本组只覆盖 authentication。该服务的租户边界仍是 WHERE IsTag=1,
/// 属 DATA-MODEL DEBT,未在此断言,也不得据此认为 tenant isolation 已收口。
///
///
public class StocktakeLabelConfirmAuthTests
{
private static readonly Type Svc = typeof(StocktakeLabelConfirmService);
[Fact]
public void Class_MustNotBeMarkedAllowAnonymous()
{
var attr = Svc.GetCustomAttribute(inherit: true);
Assert.Null(attr);
}
[Fact]
public void NoEndpoint_MayBeMarkedAllowAnonymous()
{
var offenders = Svc
.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
.Where(m => m.GetCustomAttribute(inherit: true) != null)
.Select(m => m.Name)
.ToList();
Assert.True(offenders.Count == 0, $"以下端点仍标注了 AllowAnonymous:{string.Join(", ", offenders)}");
}
///
/// 同目录其余 MissedPrint 只读服务此前已收口,一并守住,防止有人再加回来。
///
[Theory]
[InlineData(typeof(LabelQueryService))]
[InlineData(typeof(StocktakeResultService))]
[InlineData(typeof(PendingInspectionService))]
[InlineData(typeof(StockQueryService))]
public void SiblingReadOnlyServices_MustNotBeAnonymous(Type serviceType)
{
Assert.Null(serviceType.GetCustomAttribute(inherit: true));
}
///
/// 本服务必须保持只读:出现任何写端点都应让本用例失败,
/// 因为「匿名已关」的风险论证建立在「无写能力」之上。
///
[Fact]
public void Service_MustRemainReadOnly()
{
var writeVerbs = new[] { "HttpPostAttribute", "HttpPutAttribute", "HttpDeleteAttribute", "HttpPatchAttribute" };
var offenders = Svc
.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
.Where(m => m.GetCustomAttributes(inherit: true)
.Any(a => writeVerbs.Contains(a.GetType().Name)))
.Select(m => m.Name)
.ToList();
Assert.True(offenders.Count == 0, $"出现写端点:{string.Join(", ", offenders)}");
}
}