S8WatchRuleDataAccessValidatorTests.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. using Admin.NET.Plugin.AiDOP.Entity.S8;
  2. using Admin.NET.Plugin.AiDOP.Service.S8;
  3. using Admin.NET.Plugin.AiDOP.Service.S8.Rules;
  4. using Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess;
  5. using Xunit;
  6. namespace Admin.NET.Test.S8;
  7. /// <summary>
  8. /// 规则取数配置校验(T1–T4)。
  9. ///
  10. /// 分两级:
  11. /// · ValidateForSave —— dataset_code 结构 + 目录归属,不要求 Provider 已上线(允许存草稿);
  12. /// · ValidateForEnable —— 完整运行条件(委托 Enable Gate)。
  13. ///
  14. /// S8-STANDARD-DATASET-HARD-CUTOVER-1:原有的 Legacy 分组用例
  15. /// (mode=NULL 可保存 / 缺 expression 被拒 / 不得混填 dataset_code /
  16. /// RequiresDataSource / UnknownDataAccessMode)已全部删除 ——
  17. /// 它们校验的是"两种取数模式之间的边界",而现在只剩一种模式。
  18. /// 那些字段(DataAccessMode / Expression / DataSourceId)连同校验分支一并物理消失,
  19. /// 用例既写不出来、也无对应风险可守。
  20. /// </summary>
  21. public class S8WatchRuleDataAccessValidatorTests
  22. {
  23. private const string ReadyDataset = "FAKE_VALIDATOR_DATASET";
  24. private const string DefinedNoProvider = "FAKE_VALIDATOR_DATASET_NO_PROVIDER";
  25. private static S8DatasetCapabilities TimeoutCapable() => new()
  26. {
  27. SupportsTimeout = true,
  28. HasSourceObjectId = true,
  29. HasRelatedObjectCode = true,
  30. HasDueAt = true,
  31. HasStatus = true
  32. };
  33. private sealed class FakeSource : IS8DatasetDefinitionSource
  34. {
  35. public IEnumerable<S8DatasetDefinition> GetDefinitions() => new[]
  36. {
  37. new S8DatasetDefinition
  38. {
  39. DatasetCode = ReadyDataset,
  40. DisplayName = "校验测试数据集",
  41. Kind = S8DatasetKind.Object,
  42. Capabilities = TimeoutCapable()
  43. },
  44. new S8DatasetDefinition
  45. {
  46. DatasetCode = DefinedNoProvider,
  47. DisplayName = "已定义未实现数据集",
  48. Kind = S8DatasetKind.Object,
  49. Capabilities = TimeoutCapable()
  50. }
  51. };
  52. }
  53. private sealed class TimeoutOnlyProvider : IS8MonitoringDataProvider
  54. {
  55. public string DatasetCode => ReadyDataset;
  56. public S8DatasetCapabilities Capabilities => TimeoutCapable();
  57. public Task<S8MonitoringRowSet> LoadAsync(S8MonitoringDataRequest request, CancellationToken cancellationToken = default)
  58. => Task.FromResult(S8MonitoringRowSet.Empty);
  59. }
  60. private static S8DatasetCatalog Catalog() => new(new[] { new FakeSource() });
  61. private static S8DatasetEnableGate Gate() =>
  62. new(Catalog(), new S8MonitoringDataProviderRegistry(new[] { new TimeoutOnlyProvider() }));
  63. private static AdoS8WatchRule Rule(string ruleType, string datasetCode = null) => new()
  64. {
  65. RuleCode = "UT_RULE",
  66. RuleType = ruleType,
  67. DatasetCode = datasetCode
  68. };
  69. // ---------- T1 / T2 / T3:保存级 ----------
  70. [Fact]
  71. public void DatasetRule_WithoutDatasetCode_IsRejectedOnSave()
  72. {
  73. var ex = Assert.Throws<S8BizException>(() =>
  74. S8WatchRuleDataAccessValidator.ValidateForSave(
  75. Rule("TIMEOUT"), Catalog()));
  76. Assert.Contains(S8DatasetReasonCode.CodeMissing, ex.Message);
  77. }
  78. [Fact]
  79. public void DatasetRule_WithUndefinedDataset_IsRejectedOnSave()
  80. {
  81. var ex = Assert.Throws<S8BizException>(() =>
  82. S8WatchRuleDataAccessValidator.ValidateForSave(
  83. Rule("TIMEOUT", "NO_SUCH_DATASET"), Catalog()));
  84. Assert.Contains(S8DatasetReasonCode.NotDefined, ex.Message);
  85. }
  86. [Fact]
  87. public void DatasetRule_DefinedButProviderMissing_CanBeSavedAsDraft()
  88. {
  89. // 关键行为:Provider 未上线时仍可保存配置。
  90. S8WatchRuleDataAccessValidator.ValidateForSave(
  91. Rule("TIMEOUT", DefinedNoProvider), Catalog());
  92. }
  93. // ---------- T3 / T4:启用级 ----------
  94. [Fact]
  95. public void DatasetRule_DefinedButProviderMissing_CannotBeEnabled()
  96. {
  97. var ex = Assert.Throws<S8BizException>(() =>
  98. S8WatchRuleDataAccessValidator.ValidateForEnable(
  99. Rule("TIMEOUT", DefinedNoProvider),
  100. Gate(), 1, 1));
  101. Assert.Contains(S8DatasetReasonCode.ProviderNotRegistered, ex.Message);
  102. }
  103. [Fact]
  104. public void DatasetRule_WithCapabilityMismatch_CannotBeEnabled()
  105. {
  106. // TIMEOUT-only 数据集挂 OUT_OF_RANGE 规则。
  107. var ex = Assert.Throws<S8BizException>(() =>
  108. S8WatchRuleDataAccessValidator.ValidateForEnable(
  109. Rule("OUT_OF_RANGE", ReadyDataset),
  110. Gate(), 1, 1));
  111. Assert.Contains(S8DatasetReasonCode.CapabilityMismatch, ex.Message);
  112. }
  113. [Fact]
  114. public void DatasetRule_Matching_IsEnabled_WithoutAnySql()
  115. {
  116. // 标准数据集规则不需要用户填写任何 SQL。
  117. var rule = Rule("TIMEOUT", ReadyDataset);
  118. S8WatchRuleDataAccessValidator.ValidateForSave(rule, Catalog());
  119. S8WatchRuleDataAccessValidator.ValidateForEnable(rule, Gate(), 1, 1);
  120. }
  121. }