using Admin.NET.Plugin.AiDOP.SmartOps; using Xunit; namespace Admin.NET.Plugin.AiDOP.Tests.SmartOps; public class SmartDiagnosisEvidenceTests { [Theory] [InlineData("S1", "order_schedule")] [InlineData("S3", "supplier_delivery")] [InlineData("S3", "material_readiness")] [InlineData("S4", "supplier_delivery")] [InlineData("S5", "iqc_bill")] [InlineData("S6", "s6_report")] [InlineData("S7", "fqc_result")] [InlineData("S8", "s8_exception")] public void Module_ResolvesRegisteredSource(string module, string sourceKey) { Assert.Contains(SmartDiagnosisEvidenceRegistry.Resolve(module), x => x.Key == sourceKey); } [Fact] public void S9_ReusesModuleSources_NotAThirdFactChain() { var s9 = SmartDiagnosisEvidenceRegistry.Resolve("S9"); Assert.DoesNotContain(s9, x => x.Key == "s8_exception"); Assert.Equal( SmartDiagnosisEvidenceRegistry.All.Count( x => x.Key != "s8_exception" && x.MetricCodes.Count == 0), s9.Count); Assert.Contains(s9, x => x.Domain == SmartDiagnosisEvidenceDomain.Delivery || x.Domain == SmartDiagnosisEvidenceDomain.Manufacturing); Assert.Contains(s9, x => x.Domain == SmartDiagnosisEvidenceDomain.Supply); Assert.Contains(s9, x => x.Domain == SmartDiagnosisEvidenceDomain.Quality); } [Theory] [InlineData("S1_L3_001")] [InlineData("S1_L3_105")] [InlineData("S1_L4_002")] [InlineData("S1_L4_104")] public void S1_ReviewMetric_ResolvesOnlyExplicitReviewFlowSource(string metricCode) { var sources = SmartDiagnosisEvidenceRegistry.ResolveMetric("S1", metricCode); var source = Assert.Single(sources); Assert.Equal("s1_contract_review_flow", source.Key); Assert.Contains("@metricCode", source.CountSql, StringComparison.Ordinal); Assert.Contains("@metricCode", source.ItemSql, StringComparison.Ordinal); } [Theory] [InlineData("S1_L4_202")] [InlineData("S1_L2_004")] [InlineData("S1_L1_001")] [InlineData("S3_L2_001")] [InlineData("S3_L2_002")] [InlineData("S3_L2_003")] [InlineData("S3_L2_006")] [InlineData("S3_L1_003")] [InlineData("S3_L1_004")] [InlineData("S4_L1_003")] [InlineData("S5_L1_003")] [InlineData("S5_L1_004")] [InlineData("S5_L2_003")] [InlineData("S5_L2_006")] [InlineData("S5_L3_001")] [InlineData("S5_L3_003")] [InlineData("S6_L1_001")] [InlineData("S6_L1_003")] [InlineData("S6_L2_003")] [InlineData("S6_L2_004")] [InlineData("S6_L3_007")] [InlineData("S7_L1_003")] [InlineData("S7_L2_001")] [InlineData("S7_L2_003")] [InlineData("S7_L2_004")] [InlineData("S9_L1_002")] [InlineData("S9_L1_003")] [InlineData("S9_L1_004")] [InlineData("S9_L1_005")] public void UnboundMetric_DoesNotFallBackToModuleEvidence(string metricCode) { var moduleCode = metricCode.Split('_')[0]; Assert.Empty(SmartDiagnosisEvidenceRegistry.ResolveMetric(moduleCode, metricCode)); } [Fact] public void MetricBinding_RejectsCrossModuleLookup() { Assert.Empty(SmartDiagnosisEvidenceRegistry.ResolveMetric("S3", "S1_L3_001")); } [Fact] public void UnknownModule_IsUnsupported() { Assert.Contains(SmartDiagnosisEvidenceRegistry.Resolve("S8"), x => x.Key == "s8_exception"); Assert.Empty(SmartDiagnosisEvidenceRegistry.Resolve("XX")); } [Fact] public void EverySource_RequiresTenantParameter_AndForbidsClientSql() { foreach (var source in SmartDiagnosisEvidenceRegistry.All) { Assert.Contains("@tenantId", source.CountSql, StringComparison.Ordinal); Assert.Contains("@tenantId", source.ItemSql, StringComparison.Ordinal); if (source.CountSql.Contains("factory_id", StringComparison.OrdinalIgnoreCase)) { Assert.Contains("factory_id=1", source.CountSql, StringComparison.Ordinal); Assert.Contains("factory_id<>@tenantId", source.CountSql, StringComparison.Ordinal); Assert.Contains("factory_id=1", source.ItemSql, StringComparison.Ordinal); } Assert.DoesNotContain("{", source.ItemSql); Assert.DoesNotContain("+", source.CountSql); if (source.MetricCodes.Count > 0) { Assert.Contains("@metricCode", source.CountSql, StringComparison.Ordinal); Assert.Contains("@metricCode", source.ItemSql, StringComparison.Ordinal); } foreach (var filter in source.Filters) { Assert.DoesNotContain(" ", filter.SqlColumn); Assert.DoesNotContain(";", filter.SqlColumn); Assert.Matches("^[a-z0-9_]+$", filter.SqlColumn); } if (!string.IsNullOrWhiteSpace(source.DateColumn)) Assert.Matches("^[a-zA-Z0-9_]+(\\.[a-zA-Z0-9_]+)?$", source.DateColumn); if (!string.IsNullOrWhiteSpace(source.DrillQueryKey)) Assert.Matches("^[a-zA-Z0-9_]+$", source.DrillQueryKey); } } [Theory] [InlineData("S2", "S2_L2_002", "s2_order_schedule_metric", "/aidop/s2/production-scheduling/work-order-scheduling")] [InlineData("S2", "S2_L3_002", "s2_operation_schedule_metric", "/aidop/s2/operation-plan/executable-daily-plan")] [InlineData("S3", "S3_L3_006", "s3_material_readiness_metric", "/aidop/s3/supply-kanban/work-order-material-readiness")] [InlineData("S3", "S3_L3_001", "s3_purchase_request_metric", "/aidop/s3/procurement/purchase-request")] [InlineData("S3", "S3_L3_002", "s3_purchase_order_metric", "/aidop/s3/procurement/purchase-order")] [InlineData("S3", "S3_L3_004", "s3_delivery_schedule_metric", "/aidop/s3/material-plan/delivery-schedule")] [InlineData("S3", "S3_L3_005", "s3_supplier_delivery_metric", "/aidop/s3/procurement/purchase-order")] [InlineData("S4", "S4_L3_002", "s4_supplier_delivery_metric", "/aidop/s4/delivery/supplier-delivery-management")] [InlineData("S4", "S4_L1_001", "s4_delivery_cycle_metric", "/aidop/s4/delivery/supplier-shipment")] [InlineData("S4", "S4_L3_004", "s4_purchase_execution_metric", "/aidop/s4/execution-kanban/supplier-shortage-kanban")] [InlineData("S5", "S5_L1_001", "s5_material_online_cycle_metric", "/aidop/s5/inventory/inout-query")] [InlineData("S5", "S5_L1_002", "s5_material_online_fulfillment_metric", "/aidop/s5/warehouse/production-issue")] [InlineData("S5", "S5_L2_004", "s5_iqc_bill_metric", "/aidop/s5/iqc/result-list")] [InlineData("S6", "S6_L2_002", "s6_work_order_fulfillment_metric", "/aidop/s6/production-record/order-list")] [InlineData("S7", "S7_L1_001", "s7_shipment_cycle_metric", "/aidop/s7/finished-outbound/sales-shipment-notice")] [InlineData("S7", "S7_L1_002", "s7_shipment_fulfillment_metric", "/aidop/s7/finished-outbound/sales-shipment-notice")] [InlineData("S7", "S7_L2_002", "s7_fqc_result_metric", "/aidop/s7/fqc/result-list")] [InlineData("S9", "S9_L1_001", "s9_quality_return_txn_metric", "/aidop/s7/finished-outbound/sales-shipment-notice")] public void BoundMetric_ResolvesOnlyExplicitSource(string module, string metricCode, string sourceKey, string drillPath) { var sources = SmartDiagnosisEvidenceRegistry.ResolveMetric(module, metricCode); var source = Assert.Single(sources); Assert.Equal(sourceKey, source.Key); Assert.Equal(drillPath, source.DrillPath); Assert.Contains("@metricCode", source.CountSql, StringComparison.Ordinal); Assert.Contains("@metricCode", source.ItemSql, StringComparison.Ordinal); Assert.DoesNotContain(SmartDiagnosisEvidenceRegistry.Resolve(module), x => x.Key == sourceKey); } [Fact] public void S9_LeafMetric_DoesNotUseModuleUnion() { Assert.Empty(SmartDiagnosisEvidenceRegistry.ResolveMetric("S9", "S9_L1_002")); var bound = Assert.Single(SmartDiagnosisEvidenceRegistry.ResolveMetric("S9", "S9_L1_001")); Assert.Equal("s9_quality_return_txn_metric", bound.Key); Assert.Contains("้”€ๅ”ฎ้€€่ดง", bound.ItemSql, StringComparison.Ordinal); Assert.DoesNotContain(SmartDiagnosisEvidenceRegistry.Resolve("S9"), x => x.MetricCodes.Count > 0); } [Fact] public void ClientCannotRegisterArbitraryTable() { Assert.Null(SmartDiagnosisEvidenceRegistry.Get("sys_user")); Assert.Null(SmartDiagnosisEvidenceRegistry.Get("ado_smart_ops_improvement_plan;drop table")); } }