S8DatasetOnlyRuleCreationTests.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. using System.Reflection;
  2. using Microsoft.AspNetCore.Mvc;
  3. using Admin.NET.Plugin.AiDOP.Controllers.S8;
  4. using Admin.NET.Plugin.AiDOP.Entity.S8;
  5. using Admin.NET.Plugin.AiDOP.Service.S8;
  6. using Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess;
  7. using Xunit;
  8. namespace Admin.NET.Plugin.AiDOP.Tests.S8;
  9. /// <summary>
  10. /// S8-STANDARD-DATASET-HARD-CUTOVER-1:规则创建只认治理数据集。
  11. ///
  12. /// 本文件锁的是「创建链路上再也没有 SQL / 物理数据源的位置」,
  13. /// 与 <see cref="S8LegacySymbolsRemovedTests"/> 互补:那边证明类型不存在,
  14. /// 这边证明**残存的类型也不再提供入口**。
  15. /// </summary>
  16. public class S8DatasetOnlyRuleCreationTests
  17. {
  18. private static readonly Assembly PluginAssembly = typeof(AdoS8WatchRule).Assembly;
  19. // ============================================================
  20. // T7 / T8:向导与数据源入口
  21. // ============================================================
  22. /// <summary>
  23. /// T7:向导后端不得再持有任何生成 SQL 的能力。
  24. ///
  25. /// 这几个方法曾按 monitor_object.source_table + monitor_metric 字段映射拼 SELECT,
  26. /// 是 S8 最后一处「自己造 SQL 去读别人的业务表」。逐名断言而非只看行为,
  27. /// 是因为它们是 private —— 行为测试删掉一个分支就能变绿,名字还在就说明能力还在。
  28. /// </summary>
  29. [Theory]
  30. [InlineData("BuildExpression")]
  31. [InlineData("ValidateSqlIdentifier")]
  32. [InlineData("BuildSoftDeletePredicate")]
  33. public void ConfigDraftService_HasNoSqlGenerationCapability(string methodName)
  34. {
  35. Assert.Null(typeof(S8ConfigDraftService).GetMethod(
  36. methodName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public));
  37. }
  38. /// <summary>向导服务不得再注入任何能提供物理数据源的依赖。</summary>
  39. [Fact]
  40. public void ConfigDraftService_HoldsNoDataSourceDependency()
  41. {
  42. var fieldTypes = typeof(S8ConfigDraftService)
  43. .GetFields(BindingFlags.Instance | BindingFlags.NonPublic)
  44. .Select(f => f.FieldType.FullName ?? f.FieldType.Name)
  45. .ToArray();
  46. Assert.DoesNotContain(fieldTypes, t => t.Contains("AdoS8DataSource", StringComparison.Ordinal));
  47. Assert.DoesNotContain(fieldTypes, t => t.Contains("S8SqlSugarScopeFactory", StringComparison.Ordinal));
  48. // 正向:必须持有数据集目录,否则无从校验用户选的 dataset_code。
  49. Assert.Contains(fieldTypes, t => t.Contains("IS8DatasetCatalog", StringComparison.Ordinal));
  50. }
  51. /// <summary>T8:数据源 Controller / Service / Entity 三者都不存在。</summary>
  52. [Theory]
  53. [InlineData("Admin.NET.Plugin.AiDOP.Entity.S8.AdoS8DataSource")]
  54. [InlineData("Admin.NET.Plugin.AiDOP.Service.S8.S8DataSourceService")]
  55. [InlineData("Admin.NET.Plugin.AiDOP.Controllers.S8.AdoS8ConfigDataSourcesController")]
  56. [InlineData("Admin.NET.Plugin.AiDOP.Service.S8.Rules.S8DataSourceRowLoader")]
  57. [InlineData("Admin.NET.Plugin.AiDOP.Service.S8.Rules.S8SqlSugarScopeFactory")]
  58. [InlineData("Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.S8LegacySqlDataProvider")]
  59. [InlineData("Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.IS8LegacySqlDataProvider")]
  60. [InlineData("Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.S8DataAccessMode")]
  61. [InlineData("Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.S8RuleCreationPolicy")]
  62. [InlineData("Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.S8RuleCreationOrigin")]
  63. public void LegacyDataAccessType_NoLongerExists(string fullName)
  64. {
  65. Assert.Null(PluginAssembly.GetType(fullName, throwOnError: false));
  66. }
  67. // ============================================================
  68. // 数据集目录端点
  69. // ============================================================
  70. /// <summary>
  71. /// 取代数据源端点的是一个**只读**目录。二者性质不同,不是改名:
  72. /// 旧的是租户可写的物理连接配置,新的是平台级只读目录。
  73. /// 因此本用例断言它一个写动作都没有。
  74. /// </summary>
  75. [Fact]
  76. public void DatasetsController_IsReadOnly()
  77. {
  78. var actions = typeof(AdoS8ConfigDatasetsController)
  79. .GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
  80. .ToArray();
  81. Assert.NotEmpty(actions);
  82. foreach (var m in actions)
  83. {
  84. Assert.Empty(m.GetCustomAttributes<HttpPostAttribute>());
  85. Assert.Empty(m.GetCustomAttributes<HttpPutAttribute>());
  86. Assert.Empty(m.GetCustomAttributes<HttpDeleteAttribute>());
  87. Assert.Empty(m.GetCustomAttributes<HttpPatchAttribute>());
  88. }
  89. }
  90. // ============================================================
  91. // T11:Rule 01 仍然合法
  92. // ============================================================
  93. /// <summary>
  94. /// T11:Rule 01 的配置形态在删掉三个 Legacy 列之后仍然完整可表达 ——
  95. /// 它本来就只依赖 dataset_code,这也是当初把它建成 STANDARD_DATASET 的意义。
  96. /// </summary>
  97. [Fact]
  98. public void Rule01Shape_RemainsExpressible_AfterLegacyColumnsRemoved()
  99. {
  100. var rule01 = new AdoS8WatchRule
  101. {
  102. Id = 1329909460023L,
  103. RuleCode = "RULE_S4_PURCHASE_DELIVERY_DATE_DELAY",
  104. TenantId = 838257186181189L,
  105. FactoryId = 838257186320453L,
  106. RuleType = "TIMEOUT",
  107. DatasetCode = "PURCHASE_DELIVERY",
  108. Enabled = false
  109. };
  110. Assert.Equal("PURCHASE_DELIVERY", rule01.DatasetCode);
  111. Assert.False(rule01.Enabled);
  112. // 保存级校验只看 dataset_code 是否在目录内 —— Rule 01 用的正是真实业务数据集定义。
  113. var catalog = new S8DatasetCatalog(new IS8DatasetDefinitionSource[]
  114. {
  115. new Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.Providers.S8BusinessDatasetDefinitions()
  116. });
  117. S8WatchRuleDataAccessValidator.ValidateForSave(rule01, catalog);
  118. }
  119. }