S8TimeoutEvaluatorTests.cs 4.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. using Admin.NET.Plugin.AiDOP.Service.S8.Rules;
  2. using System.Text.Json;
  3. using Xunit;
  4. namespace Admin.NET.Test.S8;
  5. /// <summary>
  6. /// R2-TIMEOUT-EVALUATOR 纯逻辑回归。覆盖 params_json 解析 + dedup_key 生成稳定性。
  7. /// 不接 DB / 不接 DI;evaluator 的 SQL 与 SqlSugarRepository 路径走 run-once 端到端验证。
  8. /// </summary>
  9. public class S8TimeoutEvaluatorTests
  10. {
  11. [Fact]
  12. public void TimeoutParams_Parses_FullSpec()
  13. {
  14. var json = """
  15. {
  16. "dueAtField": "due_at",
  17. "statusField": "status",
  18. "completedStates": ["CLOSED", "DONE", "COMPLETED"],
  19. "objectCodeField": "related_object_code",
  20. "objectIdField": "source_object_id",
  21. "graceMinutes": 5,
  22. "exceptionTypeCode": "DELIVERY_DELAY"
  23. }
  24. """;
  25. var p = S8TimeoutParams.Parse(json);
  26. Assert.Equal("due_at", p.DueAtField);
  27. Assert.Equal("status", p.StatusField);
  28. Assert.Equal(new[] { "CLOSED", "DONE", "COMPLETED" }, p.CompletedStates);
  29. Assert.Equal("related_object_code", p.ObjectCodeField);
  30. Assert.Equal("source_object_id", p.ObjectIdField);
  31. Assert.Equal(5, p.GraceMinutes);
  32. Assert.Equal("DELIVERY_DELAY", p.ExceptionTypeCode);
  33. }
  34. [Fact]
  35. public void TimeoutParams_Tolerates_MissingOptionalFields()
  36. {
  37. var json = """{ "dueAtField": "due_at", "statusField": "status", "exceptionTypeCode": "PENDING_SHIPMENT" }""";
  38. var p = S8TimeoutParams.Parse(json);
  39. Assert.Equal("due_at", p.DueAtField);
  40. Assert.Equal("status", p.StatusField);
  41. Assert.Empty(p.CompletedStates);
  42. Assert.Null(p.ObjectCodeField);
  43. Assert.Null(p.ObjectIdField);
  44. Assert.Equal(0, p.GraceMinutes);
  45. Assert.Equal("PENDING_SHIPMENT", p.ExceptionTypeCode);
  46. }
  47. [Fact]
  48. public void TimeoutParams_Skips_BlankCompletedStates()
  49. {
  50. var json = """{ "completedStates": ["CLOSED", " ", "", "DONE"] }""";
  51. var p = S8TimeoutParams.Parse(json);
  52. Assert.Equal(new[] { "CLOSED", "DONE" }, p.CompletedStates);
  53. }
  54. [Fact]
  55. public void TimeoutParams_Throws_OnInvalidJson()
  56. {
  57. // evaluator 在 EvaluateAsync 内 try/catch 该异常并降级为空命中;本断言锁定异常类型契约。
  58. // ThrowsAny 而非 Throws:System.Text.Json 实际抛 JsonReaderException(JsonException 子类),
  59. // xunit v3 的 Assert.Throws<T> 是 strict 类型匹配,必须用 ThrowsAny 接受子类。
  60. Assert.ThrowsAny<JsonException>(() => S8TimeoutParams.Parse("not-a-json"));
  61. }
  62. [Fact]
  63. public void BuildDedupKey_IsStable_SameInputsSameOutput()
  64. {
  65. var k1 = S8TimeoutRuleEvaluator.BuildDedupKey(1, 1, "G01_TEST_TIMEOUT", "ORDER", "ORDER-TIMEOUT-01");
  66. var k2 = S8TimeoutRuleEvaluator.BuildDedupKey(1, 1, "G01_TEST_TIMEOUT", "ORDER", "ORDER-TIMEOUT-01");
  67. Assert.Equal(k1, k2);
  68. Assert.Equal("T1:F1:RG01_TEST_TIMEOUT:ORDER:ORDER-TIMEOUT-01", k1);
  69. }
  70. [Fact]
  71. public void BuildDedupKey_Differentiates_OnTenantFactoryRuleObject()
  72. {
  73. var baseKey = S8TimeoutRuleEvaluator.BuildDedupKey(1, 1, "R", "ORDER", "ID");
  74. Assert.NotEqual(baseKey, S8TimeoutRuleEvaluator.BuildDedupKey(2, 1, "R", "ORDER", "ID"));
  75. Assert.NotEqual(baseKey, S8TimeoutRuleEvaluator.BuildDedupKey(1, 2, "R", "ORDER", "ID"));
  76. Assert.NotEqual(baseKey, S8TimeoutRuleEvaluator.BuildDedupKey(1, 1, "R2", "ORDER", "ID"));
  77. Assert.NotEqual(baseKey, S8TimeoutRuleEvaluator.BuildDedupKey(1, 1, "R", "TASK", "ID"));
  78. Assert.NotEqual(baseKey, S8TimeoutRuleEvaluator.BuildDedupKey(1, 1, "R", "ORDER", "ID2"));
  79. }
  80. [Fact]
  81. public void BuildDedupKey_Length_FitsVarchar128()
  82. {
  83. // 实体定义 dedup_key VARCHAR(128)。本断言锁定常规标识符不溢出列长度。
  84. var k = S8TimeoutRuleEvaluator.BuildDedupKey(9999999, 9999999, "VERY_LONG_RULE_CODE_FOR_PADDING_64____", "EXTREMELY_LONG_OBJECT_TYPE______", "EXTREMELY_LONG_OBJECT_ID________");
  85. Assert.True(k.Length <= 128, $"dedup_key length {k.Length} exceeds 128: {k}");
  86. }
  87. }