From f5f8948d71b6f26f9e0b4c0b6728e339de2fba2b Mon Sep 17 00:00:00 2001 From: "821644@qq.com" <821644@qq.com> Date: Mon, 13 Apr 2026 12:47:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0AlarmService=E5=92=8CTemplate?= =?UTF-8?q?Service=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Haoliang.Tests/AlarmServiceTests.cs | 286 ++++++++++++++++++++ Haoliang.Tests/ProductionCalculatorTests.cs | 123 +++++++++ Haoliang.Tests/TemplateServiceTests.cs | 230 ++++++++++++++++ 3 files changed, 639 insertions(+) create mode 100644 Haoliang.Tests/AlarmServiceTests.cs create mode 100644 Haoliang.Tests/ProductionCalculatorTests.cs create mode 100644 Haoliang.Tests/TemplateServiceTests.cs diff --git a/Haoliang.Tests/AlarmServiceTests.cs b/Haoliang.Tests/AlarmServiceTests.cs new file mode 100644 index 0000000..e37ad94 --- /dev/null +++ b/Haoliang.Tests/AlarmServiceTests.cs @@ -0,0 +1,286 @@ +using Xunit; +using Moq; +using FluentAssertions; +using Microsoft.Extensions.Logging; +using Haoliang.Core.Services; +using Haoliang.Models.System; +using Haoliang.Data.Repositories; + +namespace Haoliang.Tests; + +public class AlarmServiceTests +{ + private readonly Mock _mockAlarmRepository; + private readonly AlarmService _alarmService; + + public AlarmServiceTests() + { + _mockAlarmRepository = new Mock(); + _alarmService = new AlarmService(_mockAlarmRepository.Object); + } + + [Fact] + public async Task GetAllAlarmsAsync_ReturnsAlarmsFromRepository() + { + var alarms = new List + { + new Alarm { Id = 1, AlarmType = "DeviceOffline", AlarmContent = "Device 1 is offline" }, + new Alarm { Id = 2, AlarmType = "DeviceError", AlarmContent = "Device 2 has error" } + }; + _mockAlarmRepository.Setup(r => r.GetAllAsync()).ReturnsAsync(alarms); + + var result = await _alarmService.GetAllAlarmsAsync(); + + result.Should().HaveCount(2); + result.Should().BeEquivalentTo(alarms); + } + + [Fact] + public async Task GetAlarmByIdAsync_ReturnsAlarm_WhenExists() + { + var alarm = new Alarm { Id = 1, AlarmType = "DeviceOffline", AlarmContent = "Device offline" }; + _mockAlarmRepository.Setup(r => r.GetByIdAsync(1)).ReturnsAsync(alarm); + + var result = await _alarmService.GetAlarmByIdAsync(1); + + result.Should().NotBeNull(); + result!.Id.Should().Be(1); + } + + [Fact] + public async Task GetAlarmByIdAsync_ReturnsNull_WhenNotExists() + { + _mockAlarmRepository.Setup(r => r.GetByIdAsync(999)).ReturnsAsync((Alarm?)null); + + var result = await _alarmService.GetAlarmByIdAsync(999); + + result.Should().BeNull(); + } + + [Fact] + public async Task CreateAlarmAsync_SetsCreatedAtAndOccurrenceTime() + { + var alarm = new Alarm { AlarmType = "DeviceOffline", AlarmContent = "Test alarm" }; + _mockAlarmRepository.Setup(r => r.AddAsync(It.IsAny())).Returns(Task.CompletedTask); + _mockAlarmRepository.Setup(r => r.SaveAsync()).ReturnsAsync(1); + + var beforeCreate = DateTime.Now; + var result = await _alarmService.CreateAlarmAsync(alarm); + var afterCreate = DateTime.Now; + + result.CreatedAt.Should().BeOnOrAfter(beforeCreate); + result.CreatedAt.Should().BeOnOrBefore(afterCreate); + result.OccurrenceTime.Should().BeOnOrAfter(beforeCreate); + result.OccurrenceTime.Should().BeOnOrBefore(afterCreate); + } + + [Fact] + public async Task DeleteAlarmAsync_ReturnsTrue_WhenAlarmExists() + { + var alarm = new Alarm { Id = 1 }; + _mockAlarmRepository.Setup(r => r.GetByIdAsync(1)).ReturnsAsync(alarm); + _mockAlarmRepository.Setup(r => r.Remove(alarm)).Verifiable(); + _mockAlarmRepository.Setup(r => r.SaveAsync()).ReturnsAsync(1); + + var result = await _alarmService.DeleteAlarmAsync(1); + + result.Should().BeTrue(); + } + + [Fact] + public async Task DeleteAlarmAsync_ReturnsFalse_WhenAlarmNotExists() + { + _mockAlarmRepository.Setup(r => r.GetByIdAsync(999)).ReturnsAsync((Alarm?)null); + + var result = await _alarmService.DeleteAlarmAsync(999); + + result.Should().BeFalse(); + } + + [Fact] + public async Task GetActiveAlarmsAsync_ReturnsUnresolvedAlarms() + { + var activeAlarms = new List + { + new Alarm { Id = 1, IsResolved = false, AlarmType = "DeviceOffline" }, + new Alarm { Id = 2, IsResolved = false, AlarmType = "DeviceError" } + }; + _mockAlarmRepository.Setup(r => r.GetActiveAlarmsAsync()).ReturnsAsync(activeAlarms); + + var result = await _alarmService.GetActiveAlarmsAsync(); + + result.Should().HaveCount(2); + } + + [Fact] + public async Task GetAlarmsByTypeAsync_ReturnsFilteredAlarms() + { + var typeAlarms = new List + { + new Alarm { Id = 1, AlarmType = "DeviceOffline" } + }; + _mockAlarmRepository.Setup(r => r.GetAlarmsByTypeAsync("DeviceOffline")).ReturnsAsync(typeAlarms); + + var result = await _alarmService.GetAlarmsByTypeAsync(AlarmType.DeviceOffline); + + result.Should().HaveCount(1); + } + + [Fact] + public async Task GetCriticalAlarmsAsync_ReturnsCriticalAlarms() + { + var criticalAlarms = new List + { + new Alarm { Id = 1, AlarmLevel = "Critical" } + }; + _mockAlarmRepository.Setup(r => r.GetAlarmsByLevelAsync("Critical")).ReturnsAsync(criticalAlarms); + + var result = await _alarmService.GetCriticalAlarmsAsync(); + + result.Should().HaveCount(1); + } +} + +public class AlarmRuleServiceTests +{ + private readonly AlarmRuleService _alarmRuleService; + + public AlarmRuleServiceTests() + { + _alarmRuleService = new AlarmRuleService(); + } + + [Fact] + public async Task GetAllAlarmRulesAsync_ReturnsCreatedRules() + { + await _alarmRuleService.CreateAlarmRuleAsync(new AlarmRule + { + RuleName = "Rule1", + Condition = "Condition1", + AlarmType = "DeviceError", + AlarmLevel = "Low" + }); + + var result = await _alarmRuleService.GetAllAlarmRulesAsync(); + + result.Should().NotBeEmpty(); + result.Should().HaveCount(1); + } + + [Fact] + public async Task CreateAlarmRuleAsync_AddsRule_WithIncrementedId() + { + var rule = new AlarmRule + { + RuleName = "High Temperature", + Condition = "Temperature > 100", + AlarmType = "DeviceError", + AlarmLevel = "Critical", + IsEnabled = true + }; + + var result = await _alarmRuleService.CreateAlarmRuleAsync(rule); + + result.Id.Should().BeGreaterThan(0); + result.CreatedAt.Should().BeCloseTo(DateTime.Now, TimeSpan.FromSeconds(1)); + } + + [Fact] + public async Task GetAlarmRuleByIdAsync_ReturnsRule_WhenExists() + { + var rule = new AlarmRule + { + RuleName = "High Temperature", + Condition = "Temperature > 100", + AlarmType = "DeviceError", + AlarmLevel = "Critical" + }; + var created = await _alarmRuleService.CreateAlarmRuleAsync(rule); + + var result = await _alarmRuleService.GetAlarmRuleByIdAsync(created.Id); + + result.Should().NotBeNull(); + result!.RuleName.Should().Be("High Temperature"); + } + + [Fact] + public async Task GetAlarmRuleByIdAsync_ReturnsNull_WhenNotExists() + { + var result = await _alarmRuleService.GetAlarmRuleByIdAsync(999); + + result.Should().BeNull(); + } + + [Fact] + public async Task UpdateAlarmRuleAsync_UpdatesRule_WhenExists() + { + var rule = new AlarmRule + { + RuleName = "Original Rule", + Condition = "Original Condition", + AlarmType = "DeviceError", + AlarmLevel = "Low" + }; + var created = await _alarmRuleService.CreateAlarmRuleAsync(rule); + + var updated = await _alarmRuleService.UpdateAlarmRuleAsync(created.Id, new AlarmRule + { + RuleName = "Updated Rule", + Condition = "Updated Condition", + AlarmType = "DeviceError", + AlarmLevel = "Critical" + }); + + updated.Should().NotBeNull(); + updated!.RuleName.Should().Be("Updated Rule"); + updated.AlarmLevel.Should().Be("Critical"); + } + + [Fact] + public async Task UpdateAlarmRuleAsync_ReturnsNull_WhenNotExists() + { + var result = await _alarmRuleService.UpdateAlarmRuleAsync(999, new AlarmRule()); + + result.Should().BeNull(); + } + + [Fact] + public async Task DeleteAlarmRuleAsync_ReturnsTrue_WhenExists() + { + var rule = new AlarmRule { RuleName = "To Delete" }; + var created = await _alarmRuleService.CreateAlarmRuleAsync(rule); + + var result = await _alarmRuleService.DeleteAlarmRuleAsync(created.Id); + + result.Should().BeTrue(); + } + + [Fact] + public async Task DeleteAlarmRuleAsync_ReturnsFalse_WhenNotExists() + { + var result = await _alarmRuleService.DeleteAlarmRuleAsync(999); + + result.Should().BeFalse(); + } + + [Fact] + public async Task SetAlarmRuleEnabledAsync_EnablesRule() + { + var rule = new AlarmRule { RuleName = "Test Rule", IsEnabled = false }; + var created = await _alarmRuleService.CreateAlarmRuleAsync(rule); + + await _alarmRuleService.SetAlarmRuleEnabledAsync(created.Id, true); + + var updated = await _alarmRuleService.GetAlarmRuleByIdAsync(created.Id); + updated!.IsEnabled.Should().BeTrue(); + } + + [Fact] + public async Task TestAlarmRuleAsync_Completes() + { + var rule = new AlarmRule { RuleName = "Test Rule" }; + var created = await _alarmRuleService.CreateAlarmRuleAsync(rule); + + await _alarmRuleService.TestAlarmRuleAsync(created.Id); + } +} \ No newline at end of file diff --git a/Haoliang.Tests/ProductionCalculatorTests.cs b/Haoliang.Tests/ProductionCalculatorTests.cs new file mode 100644 index 0000000..04db68b --- /dev/null +++ b/Haoliang.Tests/ProductionCalculatorTests.cs @@ -0,0 +1,123 @@ +using Xunit; +using Moq; +using FluentAssertions; +using Microsoft.Extensions.Logging; +using Haoliang.Core.Services; +using DeviceStatus = Haoliang.Models.Device.DeviceCurrentStatus; + +namespace Haoliang.Tests; + +public class ProductionCalculatorTests +{ + [Fact] + public void CalculateProduction_SameProgram_ReturnsDifference() + { + var current = new DeviceStatus + { + DeviceId = 1, + NCProgram = "PROGRAM001", + CumulativeCount = 150, + RecordTime = DateTime.Now + }; + + var last = new DeviceStatus + { + DeviceId = 1, + NCProgram = "PROGRAM001", + CumulativeCount = 100, + RecordTime = DateTime.Now.AddMinutes(-1) + }; + + var result = Haoliang.Models.Production.ProductionCalculator.CalculateProduction(current, last); + + result.Should().Be(50); + } + + [Fact] + public void CalculateProduction_ProgramSwitch_ReturnsCurrentCount() + { + var current = new DeviceStatus + { + DeviceId = 1, + NCProgram = "PROGRAM002", + CumulativeCount = 50, + RecordTime = DateTime.Now + }; + + var last = new DeviceStatus + { + DeviceId = 1, + NCProgram = "PROGRAM001", + CumulativeCount = 100, + RecordTime = DateTime.Now.AddMinutes(-1) + }; + + var result = Haoliang.Models.Production.ProductionCalculator.CalculateProduction(current, last); + + result.Should().Be(50); + } + + [Fact] + public void CalculateProduction_NegativeValue_ReturnsZero() + { + var current = new DeviceStatus + { + DeviceId = 1, + NCProgram = "PROGRAM001", + CumulativeCount = 50, + RecordTime = DateTime.Now + }; + + var last = new DeviceStatus + { + DeviceId = 1, + NCProgram = "PROGRAM001", + CumulativeCount = 100, + RecordTime = DateTime.Now.AddMinutes(-1) + }; + + var result = Haoliang.Models.Production.ProductionCalculator.CalculateProduction(current, last); + + result.Should().Be(0); + } + + [Fact] + public void CalculateProduction_SameProgramZeroDiff_ReturnsZero() + { + var current = new DeviceStatus + { + DeviceId = 1, + NCProgram = "PROGRAM001", + CumulativeCount = 100, + RecordTime = DateTime.Now + }; + + var last = new DeviceStatus + { + DeviceId = 1, + NCProgram = "PROGRAM001", + CumulativeCount = 100, + RecordTime = DateTime.Now.AddMinutes(-1) + }; + + var result = Haoliang.Models.Production.ProductionCalculator.CalculateProduction(current, last); + + result.Should().Be(0); + } + + [Fact] + public void IsNewProgram_DifferentPrograms_ReturnsTrue() + { + var result = Haoliang.Models.Production.ProductionCalculator.IsNewProgram("PROGRAM001", "PROGRAM002"); + + result.Should().BeTrue(); + } + + [Fact] + public void IsNewProgram_SameProgram_ReturnsFalse() + { + var result = Haoliang.Models.Production.ProductionCalculator.IsNewProgram("PROGRAM001", "PROGRAM001"); + + result.Should().BeFalse(); + } +} \ No newline at end of file diff --git a/Haoliang.Tests/TemplateServiceTests.cs b/Haoliang.Tests/TemplateServiceTests.cs new file mode 100644 index 0000000..692c710 --- /dev/null +++ b/Haoliang.Tests/TemplateServiceTests.cs @@ -0,0 +1,230 @@ +using Xunit; +using Moq; +using FluentAssertions; +using Haoliang.Core.Services; +using Haoliang.Models.Template; +using Haoliang.Data.Repositories; + +namespace Haoliang.Tests; + +public class TemplateServiceTests +{ + private readonly Mock _mockTemplateRepository; + private readonly TemplateService _templateService; + + public TemplateServiceTests() + { + _mockTemplateRepository = new Mock(); + _templateService = new TemplateService(_mockTemplateRepository.Object); + } + + [Fact] + public async Task GetAllTemplatesAsync_ReturnsTemplatesFromRepository() + { + var templates = new List + { + new CNCBrandTemplate { Id = 1, BrandName = "Fanuc", Description = "Fanuc CNC" }, + new CNCBrandTemplate { Id = 2, BrandName = "Mitsubishi", Description = "Mitsubishi CNC" } + }; + _mockTemplateRepository.Setup(r => r.GetAllAsync()).ReturnsAsync(templates); + + var result = await _templateService.GetAllTemplatesAsync(); + + result.Should().HaveCount(2); + result.Should().BeEquivalentTo(templates); + } + + [Fact] + public async Task GetTemplateByIdAsync_ReturnsTemplate_WhenExists() + { + var template = new CNCBrandTemplate { Id = 1, BrandName = "Fanuc", Description = "Fanuc CNC" }; + _mockTemplateRepository.Setup(r => r.GetByIdAsync(1)).ReturnsAsync(template); + + var result = await _templateService.GetTemplateByIdAsync(1); + + result.Should().NotBeNull(); + result!.BrandName.Should().Be("Fanuc"); + } + + [Fact] + public async Task GetTemplateByIdAsync_ReturnsNull_WhenNotExists() + { + _mockTemplateRepository.Setup(r => r.GetByIdAsync(999)).ReturnsAsync((CNCBrandTemplate?)null); + + var result = await _templateService.GetTemplateByIdAsync(999); + + result.Should().BeNull(); + } + + [Fact] + public async Task CreateTemplateAsync_SetsCreatedAtUpdatedAtAndIsEnabled() + { + var template = new CNCBrandTemplate { BrandName = "NewBrand", Description = "New CNC" }; + _mockTemplateRepository.Setup(r => r.AddAsync(It.IsAny())).Returns(Task.CompletedTask); + _mockTemplateRepository.Setup(r => r.SaveAsync()).ReturnsAsync(1); + + var beforeCreate = DateTime.Now; + var result = await _templateService.CreateTemplateAsync(template); + var afterCreate = DateTime.Now; + + result.CreatedAt.Should().BeOnOrAfter(beforeCreate); + result.CreatedAt.Should().BeOnOrBefore(afterCreate); + result.UpdatedAt.Should().BeOnOrAfter(beforeCreate); + result.UpdatedAt.Should().BeOnOrBefore(afterCreate); + result.IsEnabled.Should().BeTrue(); + } + + [Fact] + public async Task UpdateTemplateAsync_UpdatesTemplate_WhenExists() + { + var existing = new CNCBrandTemplate { Id = 1, BrandName = "Original", Description = "Original Desc" }; + _mockTemplateRepository.Setup(r => r.GetByIdAsync(1)).ReturnsAsync(existing); + _mockTemplateRepository.Setup(r => r.Update(It.IsAny())).Verifiable(); + _mockTemplateRepository.Setup(r => r.SaveAsync()).ReturnsAsync(1); + + var updated = await _templateService.UpdateTemplateAsync(1, new CNCBrandTemplate + { + BrandName = "Updated", + Description = "Updated Desc" + }); + + updated.Should().NotBeNull(); + updated!.BrandName.Should().Be("Updated"); + updated.Description.Should().Be("Updated Desc"); + } + + [Fact] + public async Task UpdateTemplateAsync_ReturnsNull_WhenNotExists() + { + _mockTemplateRepository.Setup(r => r.GetByIdAsync(999)).ReturnsAsync((CNCBrandTemplate?)null); + + var result = await _templateService.UpdateTemplateAsync(999, new CNCBrandTemplate()); + + result.Should().BeNull(); + } + + [Fact] + public async Task DeleteTemplateAsync_ReturnsTrue_WhenTemplateExists() + { + var template = new CNCBrandTemplate { Id = 1 }; + _mockTemplateRepository.Setup(r => r.GetByIdAsync(1)).ReturnsAsync(template); + _mockTemplateRepository.Setup(r => r.Remove(template)).Verifiable(); + _mockTemplateRepository.Setup(r => r.SaveAsync()).ReturnsAsync(1); + + var result = await _templateService.DeleteTemplateAsync(1); + + result.Should().BeTrue(); + } + + [Fact] + public async Task DeleteTemplateAsync_ReturnsFalse_WhenTemplateNotExists() + { + _mockTemplateRepository.Setup(r => r.GetByIdAsync(999)).ReturnsAsync((CNCBrandTemplate?)null); + + var result = await _templateService.DeleteTemplateAsync(999); + + result.Should().BeFalse(); + } + + [Fact] + public async Task EnableTemplateAsync_CallsUpdateTemplateEnabled() + { + _mockTemplateRepository.Setup(r => r.UpdateTemplateEnabledAsync(1, true)).Returns(Task.CompletedTask); + + var result = await _templateService.EnableTemplateAsync(1); + + result.Should().BeTrue(); + _mockTemplateRepository.Verify(r => r.UpdateTemplateEnabledAsync(1, true), Times.Once); + } + + [Fact] + public async Task DisableTemplateAsync_CallsUpdateTemplateEnabled() + { + _mockTemplateRepository.Setup(r => r.UpdateTemplateEnabledAsync(1, false)).Returns(Task.CompletedTask); + + var result = await _templateService.DisableTemplateAsync(1); + + result.Should().BeTrue(); + _mockTemplateRepository.Verify(r => r.UpdateTemplateEnabledAsync(1, false), Times.Once); + } + + [Fact] + public async Task CloneTemplateAsync_CreatesCopyWithNewName() + { + var original = new CNCBrandTemplate + { + Id = 1, + BrandName = "Original", + Description = "Original Description", + FieldMappings = new List + { + new TemplateFieldMapping { SourceFieldPath = "tag1", StandardFieldId = "Status" } + } + }; + _mockTemplateRepository.Setup(r => r.GetByIdAsync(1)).ReturnsAsync(original); + _mockTemplateRepository.Setup(r => r.AddAsync(It.IsAny())).Returns(Task.CompletedTask); + _mockTemplateRepository.Setup(r => r.SaveAsync()).ReturnsAsync(1); + + var result = await _templateService.CloneTemplateAsync(1, "ClonedTemplate"); + + result.Should().NotBeNull(); + result!.BrandName.Should().Be("ClonedTemplate"); + result.Description.Should().Be("Original Description (Cloned)"); + result.FieldMappings.Should().HaveCount(1); + result.IsEnabled.Should().BeFalse(); + } + + [Fact] + public async Task CloneTemplateAsync_ReturnsNull_WhenOriginalNotExists() + { + _mockTemplateRepository.Setup(r => r.GetByIdAsync(999)).ReturnsAsync((CNCBrandTemplate?)null); + + var result = await _templateService.CloneTemplateAsync(999, "NewName"); + + result.Should().BeNull(); + } + + [Fact] + public async Task GetTemplatesByBrandAsync_ReturnsMatchingTemplates() + { + var templates = new List + { + new CNCBrandTemplate { Id = 1, BrandName = "Fanuc" } + }; + _mockTemplateRepository.Setup(r => r.FindAsync(t => t.BrandName == "Fanuc")).ReturnsAsync(templates); + + var result = await _templateService.GetTemplatesByBrandAsync("Fanuc"); + + result.Should().HaveCount(1); + } + + [Fact] + public async Task GetActiveTemplatesAsync_ReturnsEnabledTemplates() + { + var activeTemplates = new List + { + new CNCBrandTemplate { Id = 1, BrandName = "Fanuc", IsEnabled = true } + }; + _mockTemplateRepository.Setup(r => r.GetEnabledTemplatesAsync()).ReturnsAsync(activeTemplates); + + var result = await _templateService.GetActiveTemplatesAsync(); + + result.Should().HaveCount(1); + } + + [Fact] + public async Task ValidateTemplateAsync_ReturnsTrue() + { + var template = new CNCBrandTemplate { BrandName = "Test", Description = "Test" }; + + var result = await _templateService.ValidateTemplateAsync(template); + + result.Should().BeTrue(); + } + + [Fact] + public async Task TestTemplateAsync_Completes() + { + await _templateService.TestTemplateAsync(1); + } +} \ No newline at end of file