using Admin.NET.Plugin.AiDOP.Service.S8.Rules;
using System.Text.Json;
using Xunit;
namespace Admin.NET.Test.S8;
///
/// R2-TIMEOUT-EVALUATOR 纯逻辑回归。覆盖 params_json 解析 + dedup_key 生成稳定性。
/// 不接 DB / 不接 DI;evaluator 的 SQL 与 SqlSugarRepository 路径走 run-once 端到端验证。
///
public class S8TimeoutEvaluatorTests
{
[Fact]
public void TimeoutParams_Parses_FullSpec()
{
var json = """
{
"dueAtField": "due_at",
"statusField": "status",
"completedStates": ["CLOSED", "DONE", "COMPLETED"],
"objectCodeField": "related_object_code",
"objectIdField": "source_object_id",
"graceMinutes": 5,
"exceptionTypeCode": "DELIVERY_DELAY"
}
""";
var p = S8TimeoutParams.Parse(json);
Assert.Equal("due_at", p.DueAtField);
Assert.Equal("status", p.StatusField);
Assert.Equal(new[] { "CLOSED", "DONE", "COMPLETED" }, p.CompletedStates);
Assert.Equal("related_object_code", p.ObjectCodeField);
Assert.Equal("source_object_id", p.ObjectIdField);
Assert.Equal(5, p.GraceMinutes);
Assert.Equal("DELIVERY_DELAY", p.ExceptionTypeCode);
}
[Fact]
public void TimeoutParams_Tolerates_MissingOptionalFields()
{
var json = """{ "dueAtField": "due_at", "statusField": "status", "exceptionTypeCode": "PENDING_SHIPMENT" }""";
var p = S8TimeoutParams.Parse(json);
Assert.Equal("due_at", p.DueAtField);
Assert.Equal("status", p.StatusField);
Assert.Empty(p.CompletedStates);
Assert.Null(p.ObjectCodeField);
Assert.Null(p.ObjectIdField);
Assert.Equal(0, p.GraceMinutes);
Assert.Equal("PENDING_SHIPMENT", p.ExceptionTypeCode);
}
[Fact]
public void TimeoutParams_Skips_BlankCompletedStates()
{
var json = """{ "completedStates": ["CLOSED", " ", "", "DONE"] }""";
var p = S8TimeoutParams.Parse(json);
Assert.Equal(new[] { "CLOSED", "DONE" }, p.CompletedStates);
}
[Fact]
public void TimeoutParams_Throws_OnInvalidJson()
{
// evaluator 在 EvaluateAsync 内 try/catch 该异常并降级为空命中;本断言锁定异常类型契约。
// ThrowsAny 而非 Throws:System.Text.Json 实际抛 JsonReaderException(JsonException 子类),
// xunit v3 的 Assert.Throws 是 strict 类型匹配,必须用 ThrowsAny 接受子类。
Assert.ThrowsAny(() => S8TimeoutParams.Parse("not-a-json"));
}
[Fact]
public void BuildDedupKey_IsStable_SameInputsSameOutput()
{
var k1 = S8TimeoutRuleEvaluator.BuildDedupKey(1, 1, "G01_TEST_TIMEOUT", "ORDER", "ORDER-TIMEOUT-01");
var k2 = S8TimeoutRuleEvaluator.BuildDedupKey(1, 1, "G01_TEST_TIMEOUT", "ORDER", "ORDER-TIMEOUT-01");
Assert.Equal(k1, k2);
Assert.Equal("T1:F1:RG01_TEST_TIMEOUT:ORDER:ORDER-TIMEOUT-01", k1);
}
[Fact]
public void BuildDedupKey_Differentiates_OnTenantFactoryRuleObject()
{
var baseKey = S8TimeoutRuleEvaluator.BuildDedupKey(1, 1, "R", "ORDER", "ID");
Assert.NotEqual(baseKey, S8TimeoutRuleEvaluator.BuildDedupKey(2, 1, "R", "ORDER", "ID"));
Assert.NotEqual(baseKey, S8TimeoutRuleEvaluator.BuildDedupKey(1, 2, "R", "ORDER", "ID"));
Assert.NotEqual(baseKey, S8TimeoutRuleEvaluator.BuildDedupKey(1, 1, "R2", "ORDER", "ID"));
Assert.NotEqual(baseKey, S8TimeoutRuleEvaluator.BuildDedupKey(1, 1, "R", "TASK", "ID"));
Assert.NotEqual(baseKey, S8TimeoutRuleEvaluator.BuildDedupKey(1, 1, "R", "ORDER", "ID2"));
}
[Fact]
public void BuildDedupKey_Length_FitsVarchar128()
{
// 实体定义 dedup_key VARCHAR(128)。本断言锁定常规标识符不溢出列长度。
var k = S8TimeoutRuleEvaluator.BuildDedupKey(9999999, 9999999, "VERY_LONG_RULE_CODE_FOR_PADDING_64____", "EXTREMELY_LONG_OBJECT_TYPE______", "EXTREMELY_LONG_OBJECT_ID________");
Assert.True(k.Length <= 128, $"dedup_key length {k.Length} exceeds 128: {k}");
}
}