SmartOpsImprovementActionTests.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. using Admin.NET.Plugin.AiDOP.Controllers;
  2. using Admin.NET.Plugin.AiDOP.Entity;
  3. using Admin.NET.Plugin.AiDOP.SmartOps;
  4. using Xunit;
  5. namespace Admin.NET.Plugin.AiDOP.Tests.SmartOps;
  6. public class SmartOpsImprovementActionTests
  7. {
  8. [Theory]
  9. [InlineData("doing", "in_progress")]
  10. [InlineData("in_progress", "in_progress")]
  11. [InlineData("done", "completed")]
  12. [InlineData("cancelled", "cancelled")]
  13. [InlineData("pending", "pending")]
  14. public void Status_NormalizesLegacyValues(string raw, string expected)
  15. {
  16. Assert.Equal(expected, SmartOpsImprovementActionStatus.Normalize(raw));
  17. }
  18. [Theory]
  19. [InlineData("pending", "in_progress", true)]
  20. [InlineData("pending", "cancelled", true)]
  21. [InlineData("in_progress", "completed", true)]
  22. [InlineData("completed", "pending", false)]
  23. [InlineData("cancelled", "completed", false)]
  24. [InlineData("cancelled", "pending", true)]
  25. public void Status_TransitionsAreGuarded(string from, string to, bool allowed)
  26. {
  27. Assert.Equal(allowed, SmartOpsImprovementActionStatus.CanTransition(from, to));
  28. }
  29. [Fact]
  30. public void Overdue_OnlyAppliesToOpenActions()
  31. {
  32. var yesterday = DateTime.Today.AddDays(-1);
  33. Assert.True(SmartOpsImprovementActionStatus.IsOverdue("pending", yesterday, DateTime.Today));
  34. Assert.False(SmartOpsImprovementActionStatus.IsOverdue("completed", yesterday, DateTime.Today));
  35. Assert.Equal("overdue", SmartOpsImprovementActionStatus.DisplayStatus("doing", yesterday, DateTime.Today));
  36. }
  37. [Fact]
  38. public void NoticeKey_IsIdempotentPerDayAndType()
  39. {
  40. var key1 = SmartOpsImprovementNoticeType.NoticeKey(1, 9, SmartOpsImprovementNoticeType.Overdue, new DateTime(2026, 8, 17));
  41. var key2 = SmartOpsImprovementNoticeType.NoticeKey(1, 9, SmartOpsImprovementNoticeType.Overdue, new DateTime(2026, 8, 17));
  42. var key3 = SmartOpsImprovementNoticeType.NoticeKey(1, 9, SmartOpsImprovementNoticeType.DueSoon, new DateTime(2026, 8, 17));
  43. Assert.Equal(key1, key2);
  44. Assert.NotEqual(key1, key3);
  45. }
  46. [Fact]
  47. public void LegacyJson_ParsesCamelAndPascal()
  48. {
  49. var camel = SmartOpsImprovementActionService.ParseDrafts(
  50. """[{"content":"核对缺料","owner":"1001","dueDate":"2026-08-18","status":"doing"}]""");
  51. var pascal = SmartOpsImprovementActionService.ParseDrafts(
  52. """[{"Content":"核对缺料","Owner":"计划员","DueDate":"2026-08-18","Status":"pending"}]""");
  53. Assert.Single(camel);
  54. Assert.Equal("核对缺料", camel[0].Content);
  55. Assert.Equal("doing", camel[0].Status);
  56. Assert.Single(pascal);
  57. Assert.Equal("计划员", pascal[0].Owner);
  58. }
  59. [Fact]
  60. public void LatestDueDate_TakesMaxValidActionDate()
  61. {
  62. var items = new List<ImprovementActionItem>
  63. {
  64. new() { Content = "a", DueDate = "2026-09-10" },
  65. new() { Content = " ", DueDate = "2026-12-01" },
  66. new() { Content = "b", DueDate = "2026-09-20" },
  67. new() { Content = "c", DueDate = "not-a-date" },
  68. };
  69. Assert.Equal(new DateTime(2026, 9, 20), SmartOpsImprovementActionService.ResolveLatestDueDate(items));
  70. Assert.Null(SmartOpsImprovementActionService.ResolveLatestDueDate(null));
  71. Assert.Null(SmartOpsImprovementActionService.ResolveLatestDueDate(new List<ImprovementActionItem>()));
  72. }
  73. [Fact]
  74. public void Controller_NoLongerWritesActionJsonAsSourceOfTruth()
  75. {
  76. var source = File.ReadAllText(FindController());
  77. Assert.Contains("_actions.MutateAsync", source);
  78. Assert.Contains("_actions.ReplaceFromDraftAsync", source);
  79. Assert.Contains("ResolveLatestDueDate(input.ActionItems)", source);
  80. Assert.DoesNotContain("DueDate = input.DueDate", source);
  81. Assert.DoesNotContain("ActionItemsJson = actionItemsJson", source);
  82. }
  83. private static string FindController()
  84. {
  85. var dir = new DirectoryInfo(AppContext.BaseDirectory);
  86. while (dir != null)
  87. {
  88. var candidate = Path.Combine(dir.FullName,
  89. "server", "Plugins", "Admin.NET.Plugin.AiDOP", "Controllers",
  90. "AdoSmartOpsImprovementPlanController.cs");
  91. if (File.Exists(candidate)) return candidate;
  92. dir = dir.Parent;
  93. }
  94. throw new FileNotFoundException("AdoSmartOpsImprovementPlanController.cs");
  95. }
  96. }