using System.Reflection; using Microsoft.AspNetCore.Mvc; using Admin.NET.Plugin.AiDOP.Controllers.S8; using Admin.NET.Plugin.AiDOP.Entity.S8; using Admin.NET.Plugin.AiDOP.Service.S8; using Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess; using Xunit; namespace Admin.NET.Plugin.AiDOP.Tests.S8; /// /// S8-STANDARD-DATASET-HARD-CUTOVER-1:规则创建只认治理数据集。 /// /// 本文件锁的是「创建链路上再也没有 SQL / 物理数据源的位置」, /// 与 互补:那边证明类型不存在, /// 这边证明**残存的类型也不再提供入口**。 /// public class S8DatasetOnlyRuleCreationTests { private static readonly Assembly PluginAssembly = typeof(AdoS8WatchRule).Assembly; // ============================================================ // T7 / T8:向导与数据源入口 // ============================================================ /// /// T7:向导后端不得再持有任何生成 SQL 的能力。 /// /// 这几个方法曾按 monitor_object.source_table + monitor_metric 字段映射拼 SELECT, /// 是 S8 最后一处「自己造 SQL 去读别人的业务表」。逐名断言而非只看行为, /// 是因为它们是 private —— 行为测试删掉一个分支就能变绿,名字还在就说明能力还在。 /// [Theory] [InlineData("BuildExpression")] [InlineData("ValidateSqlIdentifier")] [InlineData("BuildSoftDeletePredicate")] public void ConfigDraftService_HasNoSqlGenerationCapability(string methodName) { Assert.Null(typeof(S8ConfigDraftService).GetMethod( methodName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)); } /// 向导服务不得再注入任何能提供物理数据源的依赖。 [Fact] public void ConfigDraftService_HoldsNoDataSourceDependency() { var fieldTypes = typeof(S8ConfigDraftService) .GetFields(BindingFlags.Instance | BindingFlags.NonPublic) .Select(f => f.FieldType.FullName ?? f.FieldType.Name) .ToArray(); Assert.DoesNotContain(fieldTypes, t => t.Contains("AdoS8DataSource", StringComparison.Ordinal)); Assert.DoesNotContain(fieldTypes, t => t.Contains("S8SqlSugarScopeFactory", StringComparison.Ordinal)); // 正向:必须持有数据集目录,否则无从校验用户选的 dataset_code。 Assert.Contains(fieldTypes, t => t.Contains("IS8DatasetCatalog", StringComparison.Ordinal)); } /// T8:数据源 Controller / Service / Entity 三者都不存在。 [Theory] [InlineData("Admin.NET.Plugin.AiDOP.Entity.S8.AdoS8DataSource")] [InlineData("Admin.NET.Plugin.AiDOP.Service.S8.S8DataSourceService")] [InlineData("Admin.NET.Plugin.AiDOP.Controllers.S8.AdoS8ConfigDataSourcesController")] [InlineData("Admin.NET.Plugin.AiDOP.Service.S8.Rules.S8DataSourceRowLoader")] [InlineData("Admin.NET.Plugin.AiDOP.Service.S8.Rules.S8SqlSugarScopeFactory")] [InlineData("Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.S8LegacySqlDataProvider")] [InlineData("Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.IS8LegacySqlDataProvider")] [InlineData("Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.S8DataAccessMode")] [InlineData("Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.S8RuleCreationPolicy")] [InlineData("Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.S8RuleCreationOrigin")] public void LegacyDataAccessType_NoLongerExists(string fullName) { Assert.Null(PluginAssembly.GetType(fullName, throwOnError: false)); } // ============================================================ // 数据集目录端点 // ============================================================ /// /// 取代数据源端点的是一个**只读**目录。二者性质不同,不是改名: /// 旧的是租户可写的物理连接配置,新的是平台级只读目录。 /// 因此本用例断言它一个写动作都没有。 /// [Fact] public void DatasetsController_IsReadOnly() { var actions = typeof(AdoS8ConfigDatasetsController) .GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly) .ToArray(); Assert.NotEmpty(actions); foreach (var m in actions) { Assert.Empty(m.GetCustomAttributes()); Assert.Empty(m.GetCustomAttributes()); Assert.Empty(m.GetCustomAttributes()); Assert.Empty(m.GetCustomAttributes()); } } // ============================================================ // T11:Rule 01 仍然合法 // ============================================================ /// /// T11:Rule 01 的配置形态在删掉三个 Legacy 列之后仍然完整可表达 —— /// 它本来就只依赖 dataset_code,这也是当初把它建成 STANDARD_DATASET 的意义。 /// [Fact] public void Rule01Shape_RemainsExpressible_AfterLegacyColumnsRemoved() { var rule01 = new AdoS8WatchRule { Id = 1329909460023L, RuleCode = "RULE_S4_PURCHASE_DELIVERY_DATE_DELAY", TenantId = 838257186181189L, FactoryId = 838257186320453L, RuleType = "TIMEOUT", DatasetCode = "PURCHASE_DELIVERY", Enabled = false }; Assert.Equal("PURCHASE_DELIVERY", rule01.DatasetCode); Assert.False(rule01.Enabled); // 保存级校验只看 dataset_code 是否在目录内 —— Rule 01 用的正是真实业务数据集定义。 var catalog = new S8DatasetCatalog(new IS8DatasetDefinitionSource[] { new Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.Providers.S8BusinessDatasetDefinitions() }); S8WatchRuleDataAccessValidator.ValidateForSave(rule01, catalog); } }