KpiTargetServiceTests.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. using Admin.NET.Plugin.AiDOP.Dto.SmartOps;
  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 KpiTargetServiceTests
  7. {
  8. private static KpiTargetService Svc(FakeKpiTargetStore store)
  9. {
  10. store.Kpis.Add(new AdoSmartOpsKpiMaster { TenantId = 9, MetricCode = "S1_L1_001", ModuleCode = "S1", IsEnabled = true });
  11. var resolver = new KpiTargetResolver(store, new LegacyKpiTargetProvider(new FakeDailyReader(), new Microsoft.Extensions.Logging.Abstractions.NullLogger<LegacyKpiTargetProvider>()), new Microsoft.Extensions.Logging.Abstractions.NullLogger<KpiTargetResolver>());
  12. return new KpiTargetService(store, resolver, null!);
  13. }
  14. [Fact]
  15. public void Overlap_InclusiveBounds()
  16. {
  17. Assert.True(KpiTargetRules.Overlaps(new DateTime(2026, 1, 1), new DateTime(2026, 1, 31), new DateTime(2026, 1, 31), null));
  18. Assert.False(KpiTargetRules.Overlaps(new DateTime(2026, 1, 1), new DateTime(2026, 1, 30), new DateTime(2026, 1, 31), null));
  19. }
  20. [Fact]
  21. public async Task Create_RejectsNonPositive()
  22. {
  23. var svc = Svc(new FakeKpiTargetStore());
  24. await Assert.ThrowsAsync<KpiTargetValidationException>(() => svc.CreateCoreAsync(9, new KpiTargetCreateDto
  25. {
  26. MetricCode = "S1_L1_001", FactoryId = 1, TargetValue = 0, EffectiveFrom = new DateTime(2026, 9, 1)
  27. }, KpiTargetConfigSource.Manual, null, 1));
  28. }
  29. [Fact]
  30. public async Task Create_RejectsCrossTenantMetric()
  31. {
  32. var svc = Svc(new FakeKpiTargetStore());
  33. await Assert.ThrowsAsync<KpiTargetNotFoundException>(() => svc.CreateCoreAsync(99, new KpiTargetCreateDto
  34. {
  35. MetricCode = "S1_L1_001", FactoryId = 1, TargetValue = 3, EffectiveFrom = new DateTime(2026, 9, 1)
  36. }, KpiTargetConfigSource.Manual, null, 1));
  37. }
  38. [Fact]
  39. public async Task Create_RejectsOverlap()
  40. {
  41. var store = new FakeKpiTargetStore();
  42. var svc = Svc(store);
  43. await svc.CreateCoreAsync(9, new KpiTargetCreateDto
  44. {
  45. MetricCode = "S1_L1_001", FactoryId = 1, TargetValue = 3, EffectiveFrom = new DateTime(2026, 9, 1)
  46. }, KpiTargetConfigSource.Manual, null, 1);
  47. await Assert.ThrowsAsync<KpiTargetConflictException>(() => svc.CreateCoreAsync(9, new KpiTargetCreateDto
  48. {
  49. MetricCode = "S1_L1_001", FactoryId = 1, TargetValue = 4, EffectiveFrom = new DateTime(2026, 9, 15)
  50. }, KpiTargetConfigSource.Manual, null, 1));
  51. }
  52. [Fact]
  53. public async Task Create_ClosePrevious_AllowsFuture()
  54. {
  55. var store = new FakeKpiTargetStore();
  56. var svc = Svc(store);
  57. await svc.CreateCoreAsync(9, new KpiTargetCreateDto
  58. {
  59. MetricCode = "S1_L1_001", FactoryId = 1, TargetValue = 3, EffectiveFrom = new DateTime(2026, 1, 1)
  60. }, KpiTargetConfigSource.Manual, null, 1);
  61. var next = await svc.CreateCoreAsync(9, new KpiTargetCreateDto
  62. {
  63. MetricCode = "S1_L1_001", FactoryId = 1, TargetValue = 4, EffectiveFrom = new DateTime(2026, 9, 1), ClosePrevious = true
  64. }, KpiTargetConfigSource.Manual, null, 1);
  65. Assert.Equal(4m, next.TargetValue);
  66. Assert.Equal(new DateTime(2026, 8, 31), store.Configs[0].EffectiveTo);
  67. }
  68. [Fact]
  69. public async Task Update_EffectiveTargetValue_Rejected()
  70. {
  71. var store = new FakeKpiTargetStore();
  72. var svc = Svc(store);
  73. var created = await svc.CreateCoreAsync(9, new KpiTargetCreateDto
  74. {
  75. MetricCode = "S1_L1_001", FactoryId = 1, TargetValue = 3, EffectiveFrom = DateTime.Today.AddDays(-1)
  76. }, KpiTargetConfigSource.Manual, null, 1);
  77. await Assert.ThrowsAsync<KpiTargetConflictException>(() => svc.UpdateCoreAsync(9, created.Id, new KpiTargetUpdateDto { TargetValue = 9 }));
  78. }
  79. }