| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586 |
- using System.Reflection;
- using Admin.NET.Plugin.AiDOP.Entity.S8;
- using Admin.NET.Plugin.AiDOP.Service.S8.Rules;
- using Xunit;
- namespace Admin.NET.Plugin.AiDOP.Tests.S8;
- /// <summary>
- /// S8-STANDARD-DATASET-HARD-CUTOVER-1:Legacy 符号**不存在**契约。
- ///
- /// 与上一阶段的区别必须说清楚,否则这个文件会被误当成重复覆盖:
- /// · 退役阶段断言的是「Legacy 被拒绝」——类型还在,只是调用会抛 410 / legacy_sql_retired;
- /// · 本阶段断言的是「Legacy 根本不存在」——类型、Provider、参数、表全部消失。
- /// 前者的测试已随实现一并删除;保留它们只会让人以为 Legacy 仍是一条需要防守的路径。
- ///
- /// 用**运行期反射按名字查找**而非编译期引用:已删除的类型写不进 typeof(),
- /// 编译期引用等于永远无法表达「它不存在」这个断言。
- /// 名字用完全限定名,避免同名新类型意外让断言变绿。
- /// </summary>
- public class S8LegacySymbolsRemovedTests
- {
- private static readonly Assembly PluginAssembly = typeof(AdoS8WatchRule).Assembly;
- private static Type? Find(string fullName) => PluginAssembly.GetType(fullName, throwOnError: false);
- /// <summary>
- /// 全量 Legacy 类型清单。任何一条复活都会让本用例红——
- /// 这正是目的:Hard Cutover 之后不接受「先加回来再说」。
- /// </summary>
- public static TheoryData<string> RemovedTypes() => new()
- {
- // 报警规则(历史只读壳,Runtime Authority 一直是 AdoS8WatchRule)
- "Admin.NET.Plugin.AiDOP.Entity.S8.AdoS8AlertRule",
- "Admin.NET.Plugin.AiDOP.Service.S8.S8AlertRuleService",
- "Admin.NET.Plugin.AiDOP.Controllers.S8.AdoS8ConfigAlertRulesController",
- };
- [Theory]
- [MemberData(nameof(RemovedTypes))]
- public void LegacyType_NoLongerExists(string fullName)
- {
- Assert.Null(Find(fullName));
- }
- /// <summary>
- /// T10:evaluator 契约不得再出现 alertRules 死参数。
- ///
- /// 该形参存在期间,三个实现的方法体读取次数均为 0,调度器查库后转手丢弃——
- /// 一个纯粹的假依赖,却让每个 tick 多打一次全表查询,也误导读者以为告警阈值来自那张表。
- /// </summary>
- [Fact]
- public void EvaluatorContract_HasNoAlertRulesParameter()
- {
- var evaluate = typeof(IS8RuleEvaluator).GetMethod(nameof(IS8RuleEvaluator.EvaluateAsync))!;
- var names = evaluate.GetParameters().Select(p => p.Name).ToArray();
- Assert.DoesNotContain("alertRules", names);
- Assert.Equal(new[] { "tenantId", "factoryId", "rule", "cancellationToken" }, names);
- }
- /// <summary>
- /// 所有实现必须与接口同步收窄。只查接口不够——
- /// 实现类若保留旧重载,调用方仍可能绑到它。
- /// </summary>
- [Fact]
- public void AllEvaluatorImplementations_HaveNoAlertRulesParameter()
- {
- var implementations = PluginAssembly.GetTypes()
- .Where(t => t is { IsClass: true, IsAbstract: false } && typeof(IS8RuleEvaluator).IsAssignableFrom(t))
- .ToArray();
- // 防止「一个实现都没找到」时本用例空转通过。
- Assert.NotEmpty(implementations);
- foreach (var impl in implementations)
- {
- var overloads = impl.GetMethods(BindingFlags.Public | BindingFlags.Instance)
- .Where(m => m.Name == nameof(IS8RuleEvaluator.EvaluateAsync))
- .ToArray();
- Assert.NotEmpty(overloads);
- foreach (var m in overloads)
- Assert.DoesNotContain(m.GetParameters(), p => p.Name == "alertRules");
- }
- }
- }
|