| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495 |
- using Admin.NET.Plugin.AiDOP.Service.S8.Rules;
- using System.Text.Json;
- using Xunit;
- namespace Admin.NET.Test.S8;
- /// <summary>
- /// R2-TIMEOUT-EVALUATOR 纯逻辑回归。覆盖 params_json 解析 + dedup_key 生成稳定性。
- /// 不接 DB / 不接 DI;evaluator 的 SQL 与 SqlSugarRepository 路径走 run-once 端到端验证。
- /// </summary>
- 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<T> 是 strict 类型匹配,必须用 ThrowsAny 接受子类。
- Assert.ThrowsAny<JsonException>(() => 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}");
- }
- }
|