using Admin.NET.Plugin.AiDOP.Entity.S8;
using Admin.NET.Plugin.AiDOP.Service.S8;
using Admin.NET.Plugin.AiDOP.Service.S8.Rules;
using Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess;
using Xunit;
namespace Admin.NET.Test.S8;
///
/// 规则取数配置校验(T1–T4)。
///
/// 分两级:
/// · ValidateForSave —— dataset_code 结构 + 目录归属,不要求 Provider 已上线(允许存草稿);
/// · ValidateForEnable —— 完整运行条件(委托 Enable Gate)。
///
/// S8-STANDARD-DATASET-HARD-CUTOVER-1:原有的 Legacy 分组用例
/// (mode=NULL 可保存 / 缺 expression 被拒 / 不得混填 dataset_code /
/// RequiresDataSource / UnknownDataAccessMode)已全部删除 ——
/// 它们校验的是"两种取数模式之间的边界",而现在只剩一种模式。
/// 那些字段(DataAccessMode / Expression / DataSourceId)连同校验分支一并物理消失,
/// 用例既写不出来、也无对应风险可守。
///
public class S8WatchRuleDataAccessValidatorTests
{
private const string ReadyDataset = "FAKE_VALIDATOR_DATASET";
private const string DefinedNoProvider = "FAKE_VALIDATOR_DATASET_NO_PROVIDER";
private static S8DatasetCapabilities TimeoutCapable() => new()
{
SupportsTimeout = true,
HasSourceObjectId = true,
HasRelatedObjectCode = true,
HasDueAt = true,
HasStatus = true
};
private sealed class FakeSource : IS8DatasetDefinitionSource
{
public IEnumerable GetDefinitions() => new[]
{
new S8DatasetDefinition
{
DatasetCode = ReadyDataset,
DisplayName = "校验测试数据集",
Kind = S8DatasetKind.Object,
Capabilities = TimeoutCapable()
},
new S8DatasetDefinition
{
DatasetCode = DefinedNoProvider,
DisplayName = "已定义未实现数据集",
Kind = S8DatasetKind.Object,
Capabilities = TimeoutCapable()
}
};
}
private sealed class TimeoutOnlyProvider : IS8MonitoringDataProvider
{
public string DatasetCode => ReadyDataset;
public S8DatasetCapabilities Capabilities => TimeoutCapable();
public Task LoadAsync(S8MonitoringDataRequest request, CancellationToken cancellationToken = default)
=> Task.FromResult(S8MonitoringRowSet.Empty);
}
private static S8DatasetCatalog Catalog() => new(new[] { new FakeSource() });
private static S8DatasetEnableGate Gate() =>
new(Catalog(), new S8MonitoringDataProviderRegistry(new[] { new TimeoutOnlyProvider() }));
private static AdoS8WatchRule Rule(string ruleType, string datasetCode = null) => new()
{
RuleCode = "UT_RULE",
RuleType = ruleType,
DatasetCode = datasetCode
};
// ---------- T1 / T2 / T3:保存级 ----------
[Fact]
public void DatasetRule_WithoutDatasetCode_IsRejectedOnSave()
{
var ex = Assert.Throws(() =>
S8WatchRuleDataAccessValidator.ValidateForSave(
Rule("TIMEOUT"), Catalog()));
Assert.Contains(S8DatasetReasonCode.CodeMissing, ex.Message);
}
[Fact]
public void DatasetRule_WithUndefinedDataset_IsRejectedOnSave()
{
var ex = Assert.Throws(() =>
S8WatchRuleDataAccessValidator.ValidateForSave(
Rule("TIMEOUT", "NO_SUCH_DATASET"), Catalog()));
Assert.Contains(S8DatasetReasonCode.NotDefined, ex.Message);
}
[Fact]
public void DatasetRule_DefinedButProviderMissing_CanBeSavedAsDraft()
{
// 关键行为:Provider 未上线时仍可保存配置。
S8WatchRuleDataAccessValidator.ValidateForSave(
Rule("TIMEOUT", DefinedNoProvider), Catalog());
}
// ---------- T3 / T4:启用级 ----------
[Fact]
public void DatasetRule_DefinedButProviderMissing_CannotBeEnabled()
{
var ex = Assert.Throws(() =>
S8WatchRuleDataAccessValidator.ValidateForEnable(
Rule("TIMEOUT", DefinedNoProvider),
Gate(), 1, 1));
Assert.Contains(S8DatasetReasonCode.ProviderNotRegistered, ex.Message);
}
[Fact]
public void DatasetRule_WithCapabilityMismatch_CannotBeEnabled()
{
// TIMEOUT-only 数据集挂 OUT_OF_RANGE 规则。
var ex = Assert.Throws(() =>
S8WatchRuleDataAccessValidator.ValidateForEnable(
Rule("OUT_OF_RANGE", ReadyDataset),
Gate(), 1, 1));
Assert.Contains(S8DatasetReasonCode.CapabilityMismatch, ex.Message);
}
[Fact]
public void DatasetRule_Matching_IsEnabled_WithoutAnySql()
{
// 标准数据集规则不需要用户填写任何 SQL。
var rule = Rule("TIMEOUT", ReadyDataset);
S8WatchRuleDataAccessValidator.ValidateForSave(rule, Catalog());
S8WatchRuleDataAccessValidator.ValidateForEnable(rule, Gate(), 1, 1);
}
}