Просмотр исходного кода

test(s8): relax trusted scope contract signature check

ConfigWrites_RequireTrustedScope 对 S8WatchRuleService 用例一直失败:
断言用 GetMethod("CreateAsync", [entity, S8TrustedScope]) 精确匹配 2 个形参,
而 S8-LEGACY-SQL-RESIDUAL-CLEANUP-3(1.0.480)已给该方法追加可选形参
origin(string origin = S8RuleCreationOrigin.ExternalApi),精确匹配遂返回 null。

这是测试表达方式陈旧,不是业务缺陷:origin 是新增 SQL 技术债治理所需,
CreateAsync 的作用域约束本身没有被削弱。因此只改测试,不动业务签名、不删 origin。

改为前缀匹配 HasScopedWriteEntry:要求 CreateAsync 以
(entity, S8TrustedScope) 开头,且 S8TrustedScope 不可省略——
调用方仍无法只传实体完成写入。UpdateAsync / DeleteAsync 维持原精确匹配,
"旧的无作用域重载必须消失" 的反向断言也原样保留,覆盖范围不变。

test-only:不改变任何运行时行为,故不升版本号。
YY968XX 1 день назад
Родитель
Сommit
f66b6c0988

+ 26 - 1
server/Plugins/Admin.NET.Plugin.AiDOP.Tests/S8/S8TenantIsolationContractTests.cs

@@ -81,7 +81,16 @@ public class S8TenantIsolationContractTests
     [InlineData(typeof(S8WatchRuleService), typeof(AdoS8WatchRule))]
     public void ConfigWrites_RequireTrustedScope(Type serviceType, Type entityType)
     {
-        Assert.NotNull(serviceType.GetMethod("CreateAsync", [entityType, typeof(S8TrustedScope)]));
+        // CreateAsync 用「前两个形参匹配」而非精确签名匹配:
+        // S8-LEGACY-SQL-RESIDUAL-CLEANUP-3 给 S8WatchRuleService.CreateAsync 追加了
+        // 可选形参 origin(string origin = S8RuleCreationOrigin.ExternalApi),
+        // 精确 2 参的 GetMethod 会返回 null,导致本用例误报。
+        // 真正要保证的契约是「entity + S8TrustedScope 必须显式出现在最前」,
+        // 后置可选形参不破坏该契约,故放宽为前缀匹配。
+        Assert.True(
+            HasScopedWriteEntry(serviceType, "CreateAsync", entityType),
+            $"{serviceType.Name}.CreateAsync 必须以 ({entityType.Name}, S8TrustedScope) 开头");
+
         Assert.NotNull(serviceType.GetMethod("UpdateAsync", [typeof(long), entityType, typeof(S8TrustedScope)]));
         Assert.NotNull(serviceType.GetMethod("DeleteAsync", [typeof(long), typeof(S8TrustedScope)]));
 
@@ -91,6 +100,22 @@ public class S8TenantIsolationContractTests
         Assert.Null(serviceType.GetMethod("DeleteAsync", [typeof(long)]));
     }
 
+    /// <summary>
+    /// 写入口是否以 (entity, S8TrustedScope) 开头。允许其后存在形参(如可选的 origin),
+    /// 但作用域必须是第 2 个形参且不可省略——调用方无法只传实体就完成写入。
+    /// </summary>
+    private static bool HasScopedWriteEntry(Type serviceType, string methodName, Type entityType) =>
+        serviceType.GetMethods(BindingFlags.Instance | BindingFlags.Public)
+            .Where(m => m.Name == methodName)
+            .Any(m =>
+            {
+                var p = m.GetParameters();
+                return p.Length >= 2
+                       && p[0].ParameterType == entityType
+                       && p[1].ParameterType == typeof(S8TrustedScope)
+                       && !p[1].IsOptional;
+            });
+
     /// <summary>监视规则的每个按 Id 的运行态动作都必须带可信作用域。</summary>
     [Theory]
     [InlineData("RunNowAsync")]