using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Xunit; using Moq; using Haoliang.Core.Services; using Haoliang.Data.Repositories; using Haoliang.Models.Models.Device; using Haoliang.Models.Models.Production; using Haoliang.Models.Models.System; namespace Haoliang.Tests.Services { public class ProductionStatisticsServiceTests { private readonly Mock _mockProductionRepository; private readonly Mock _mockDeviceRepository; private readonly Mock _mockSystemRepository; private readonly Mock _mockAlarmRepository; private readonly Mock _mockCollectionRepository; private readonly ProductionStatisticsService _statisticsService; public ProductionStatisticsServiceTests() { _mockProductionRepository = new Mock(); _mockDeviceRepository = new Mock(); _mockSystemRepository = new Mock(); _mockAlarmRepository = new Mock(); _mockCollectionRepository = new Mock(); _statisticsService = new ProductionStatisticsService( _mockProductionRepository.Object, _mockDeviceRepository.Object, _mockSystemRepository.Object, _mockAlarmRepository.Object, _mockCollectionRepository.Object ); } [Fact] public async Task CalculateProductionTrendsAsync_ValidDeviceAndDates_ReturnsTrendAnalysis() { // Arrange var deviceId = 1; var startDate = DateTime.Now.AddDays(-7); var endDate = DateTime.Now; var device = new CNCDevice { Id = deviceId, Name = "Test Device" }; var productionRecords = new List { new ProductionRecord { Id = 1, DeviceId = deviceId, Created = startDate, Quantity = 100, TargetQuantity = 120 }, new ProductionRecord { Id = 2, DeviceId = deviceId, Created = startDate.AddDays(1), Quantity = 110, TargetQuantity = 120 }, new ProductionRecord { Id = 3, DeviceId = deviceId, Created = startDate.AddDays(2), Quantity = 130, TargetQuantity = 120 } }; _mockDeviceRepository.Setup(repo => repo.GetByIdAsync(deviceId)) .ReturnsAsync(device); _mockProductionRepository.Setup(repo => repo.GetProductionRecordsByDeviceAndDateRangeAsync(deviceId, startDate, endDate)) .ReturnsAsync(productionRecords); _mockSystemRepository.Setup(repo => repo.GetSystemConfigurationAsync()) .ReturnsAsync(new SystemConfiguration { DailyProductionTarget = 100 }); // Act var result = await _statisticsService.CalculateProductionTrendsAsync(deviceId, startDate, endDate); // Assert Assert.NotNull(result); Assert.Equal(deviceId, result.DeviceId); Assert.Equal(device.Name, result.DeviceName); Assert.Equal(startDate, result.PeriodStart); Assert.Equal(endDate, result.PeriodEnd); Assert.Equal(340, result.TotalProduction); Assert.Equal(3, result.AverageDailyProduction); Assert.Equal(3, result.DailyData.Count); } [Fact] public async Task CalculateProductionTrendsAsync_InvalidDevice_ThrowsKeyNotFoundException() { // Arrange var deviceId = 999; var startDate = DateTime.Now.AddDays(-7); var endDate = DateTime.Now; _mockDeviceRepository.Setup(repo => repo.GetByIdAsync(deviceId)) .ReturnsAsync((CNCDevice)null); // Act & Assert await Assert.ThrowsAsync(() => _statisticsService.CalculateProductionTrendsAsync(deviceId, startDate, endDate)); } [Fact] public async Task GenerateProductionReportAsync_ValidFilter_ReturnsProductionReport() { // Arrange var filter = new ReportFilter { DeviceIds = new List { 1 }, StartDate = DateTime.Now.AddDays(-7), EndDate = DateTime.Now, ReportType = ReportType.Daily }; var device = new CNCDevice { Id = 1, Name = "Test Device" }; var records = new List { new ProductionRecord { Id = 1, DeviceId = 1, Created = DateTime.Now, Quantity = 100, TargetQuantity = 120, IsGood = true }, new ProductionRecord { Id = 2, DeviceId = 1, Created = DateTime.Now, Quantity = 50, TargetQuantity = 60, IsGood = true } }; _mockDeviceRepository.Setup(repo => repo.GetByIdAsync(1)) .ReturnsAsync(device); _mockProductionRepository.Setup(repo => repo.GetProductionRecordsByFilterAsync(filter)) .ReturnsAsync(records); _mockSystemRepository.Setup(repo => repo.GetSystemConfigurationAsync()) .ReturnsAsync(new SystemConfiguration { DailyProductionTarget = 100 }); // Act var result = await _statisticsService.GenerateProductionReportAsync(filter); // Assert Assert.NotNull(result); Assert.Equal(ReportType.Daily, result.ReportType); Assert.Equal(2, result.SummaryItems.Count); var summary = result.SummaryItems.First(); Assert.Equal(1, summary.DeviceId); Assert.Equal("Test Device", summary.DeviceName); Assert.Equal(150, summary.Quantity); Assert.Equal(180, summary.TargetQuantity); Assert.Equal(83.33m, summary.Efficiency); } [Fact] public async Task GetDashboardSummaryAsync_ValidFilter_ReturnsDashboardSummary() { // Arrange var filter = new DashboardFilter { Date = DateTime.Today, IncludeAlerts = true }; var device = new CNCDevice { Id = 1, Name = "Test Device" }; var deviceSummaries = new List { new DeviceSummary { DeviceId = 1, DeviceName = "Test Device", TodayProduction = 100, Efficiency = 85, QualityRate = 98 } }; var alertSummaries = new List { new AlertSummary { AlertId = 1, DeviceName = "Test Device", AlertType = AlertType.DeviceOffline, Message = "Device offline" } }; _mockDeviceRepository.Setup(repo => repo.GetAllDevicesAsync()) .ReturnsAsync(new List { device }); _mockDeviceRepository.Setup(repo => repo.GetByIdAsync(1)) .ReturnsAsync(device); _mockCollectionRepository.Setup(repo => GetDeviceCurrentStatusAsync(1)) .ReturnsAsync(new DeviceCurrentStatus { Status = DeviceStatus.Online, Runtime = TimeSpan.FromHours(8) }); _mockProductionRepository.Setup(repo => repo.GetProductionRecordsByDeviceAndDateAsync(1, DateTime.Today)) .ReturnsAsync(new List()); _mockAlarmRepository.Setup(repo => GetActiveAlertsByDeviceAsync(1)) .ReturnsAsync(new List { new Alert { Id = 1, DeviceId = 1, AlertType = "DeviceOffline", Message = "Device offline" } }); // Act var result = await _statisticsService.GetDashboardSummaryAsync(filter); // Assert Assert.NotNull(result); Assert.Equal(DateTime.Now.Date, result.GeneratedAt.Date); Assert.Equal(1, result.TotalDevices); Assert.Equal(1, result.ActiveDevices); Assert.Equal(0, result.OfflineDevices); Assert.Equal(1, result.DeviceSummaries.Count); Assert.Equal(1, result.ActiveAlerts.Count); } [Fact] public async Task CalculateOeeAsync_ValidDeviceAndDate_ReturnsOeeMetrics() { // Arrange var deviceId = 1; var date = DateTime.Today; var device = new CNCDevice { Id = deviceId, Name = "Test Device", IdealCycleTime = 2 }; var records = new List { new ProductionRecord { Id = 1, DeviceId = deviceId, Created = date, Quantity = 50, TargetQuantity = 60, IsGood = true } }; _mockDeviceRepository.Setup(repo => repo.GetByIdAsync(deviceId)) .ReturnsAsync(device); _mockProductionRepository.Setup(repo => repo.GetProductionRecordsByDeviceAndDateAsync(deviceId, date)) .ReturnsAsync(records); _mockSystemRepository.Setup(repo => repo.GetSystemConfigurationAsync()) .ReturnsAsync(new SystemConfiguration { DailyWorkingHours = TimeSpan.FromHours(8) }); _mockCollectionRepository.Setup(repo => GetDeviceStatusHistoryAsync(deviceId, date, date)) .ReturnsAsync(new List()); // Act var result = await _statisticsService.CalculateOeeAsync(deviceId, date); // Assert Assert.NotNull(result); Assert.Equal(deviceId, result.DeviceId); Assert.Equal(device.Name, result.DeviceName); Assert.Equal(date, result.Date); Assert.True(result.Availability >= 0 && result.Availability <= 100); Assert.True(result.Performance >= 0 && result.Performance <= 100); Assert.True(result.Quality >= 0 && result.Quality <= 100); Assert.True(result.Oee >= 0 && result.Oee <= 100); } [Fact] public async Task GenerateProductionForecastAsync_ValidFilter_ReturnsProductionForecast() { // Arrange var filter = new ForecastFilter { DeviceId = 1, DaysToForecast = 7, Model = ForecastModel.Linear, HistoricalDataStart = DateTime.Now.AddDays(-30), HistoricalDataEnd = DateTime.Now }; var device = new CNCDevice { Id = 1, Name = "Test Device" }; var historicalData = new List { new ProductionRecord { Id = 1, DeviceId = 1, Created = DateTime.Now.AddDays(-30), Quantity = 100 }, new ProductionRecord { Id = 2, DeviceId = 1, Created = DateTime.Now.AddDays(-29), Quantity = 110 }, new ProductionRecord { Id = 3, DeviceId = 1, Created = DateTime.Now.AddDays(-28), Quantity = 105 } }; _mockDeviceRepository.Setup(repo => repo.GetByIdAsync(1)) .ReturnsAsync(device); _mockProductionRepository.Setup(repo => repo.GetProductionRecordsByDeviceAndDateRangeAsync(1, filter.HistoricalDataStart, filter.HistoricalDataEnd)) .ReturnsAsync(historicalData); // Act var result = await _statisticsService.GenerateProductionForecastAsync(filter); // Assert Assert.NotNull(result); Assert.Equal(filter.DeviceId, result.DeviceId); Assert.Equal(device.Name, result.DeviceName); Assert.Equal(filter.Model, result.ModelUsed); Assert.Equal(7, result.DailyForecasts.Count); // Check that forecasts have reasonable values foreach (var forecast in result.DailyForecasts) { Assert.True(forecast.ForecastedQuantity >= 0); Assert.True(forecast.ConfidenceLower <= forecast.ForecastedQuantity); Assert.True(forecast.ConfidenceUpper >= forecast.ForecastedQuantity); } } [Fact] public async Task DetectProductionAnomaliesAsync_ValidFilter_ReturnsAnomalyAnalysis() { // Arrange var filter = new AnomalyFilter { DeviceIds = new List { 1 }, StartDate = DateTime.Now.AddDays(-7), EndDate = DateTime.Now, MinSeverity = AnomalySeverity.Medium }; var device = new CNCDevice { Id = 1, Name = "Test Device" }; var records = new List { new ProductionRecord { Id = 1, DeviceId = 1, Created = DateTime.Now.AddDays(-2), Quantity = 100 }, new ProductionRecord { Id = 2, DeviceId = 1, Created = DateTime.Now.AddDays(-1), Quantity = 30 }, // Big drop new ProductionRecord { Id = 3, DeviceId = 1, Created = DateTime.Now, Quantity = 95 } }; _mockDeviceRepository.Setup(repo => repo.GetByIdAsync(1)) .ReturnsAsync(device); _mockProductionRepository.Setup(repo => repo.GetProductionRecordsByDeviceAndDateRangeAsync(1, filter.StartDate, filter.EndDate)) .ReturnsAsync(records); // Act var result = await _statisticsService.DetectProductionAnomaliesAsync(filter); // Assert Assert.NotNull(result); Assert.Equal(1, result.DeviceIds.Count); Assert.True(result.Anomalies.Count >= 1); // Should detect the production drop var anomaly = result.Anomalies.FirstOrDefault(a => a.Type == AnomalyType.ProductionDrop); Assert.NotNull(anomaly); Assert.Equal(AnomalySeverity.High, anomaly.Severity); } [Fact] public async Task GetEfficiencyMetricsAsync_ValidFilter_ReturnsEfficiencyMetrics() { // Arrange var filter = new EfficiencyFilter { DeviceIds = new List { 1 }, StartDate = DateTime.Now.AddDays(-7), EndDate = DateTime.Now, Metrics = EfficiencyMetric.Oee }; var device = new CNCDevice { Id = 1, Name = "Test Device" }; var records = new List { new ProductionRecord { Id = 1, DeviceId = 1, Created = DateTime.Now.AddDays(-1), Quantity = 100, TargetQuantity = 120, IsGood = true } }; _mockDeviceRepository.Setup(repo => repo.GetByIdAsync(1)) .ReturnsAsync(device); _mockDeviceRepository.Setup(repo => repo.GetAllActiveDevicesAsync()) .ReturnsAsync(new List { device }); _mockProductionRepository.Setup(repo => repo.GetProductionRecordsByDeviceAndDateRangeAsync(1, filter.StartDate, filter.EndDate)) .ReturnsAsync(records); // Act var result = await _statisticsService.CalculateEfficiencyMetricsAsync(filter); // Assert Assert.NotNull(result); Assert.Single(result.DeviceIds); Assert.Equal(1, result.DeviceIds.First()); Assert.True(result.Availability >= 0 && result.Availability <= 100); Assert.True(result.Performance >= 0 && result.Performance <= 100); Assert.True(result.Quality >= 0 && result.Quality <= 100); Assert.True(result.Oee >= 0 && result.Oee <= 100); } [Fact] public async Task PerformQualityAnalysisAsync_ValidFilter_ReturnsQualityAnalysis() { // Arrange var filter = new QualityFilter { DeviceIds = new List { 1 }, StartDate = DateTime.Now.AddDays(-7), EndDate = DateTime.Now, MetricType = QualityMetricType.DefectRate }; var device = new CNCDevice { Id = 1, Name = "Test Device" }; var records = new List { new ProductionRecord { Id = 1, DeviceId = 1, Created = DateTime.Now, Quantity = 100, TargetQuantity = 120, IsGood = true }, new ProductionRecord { Id = 2, DeviceId = 1, Created = DateTime.Now, Quantity = 20, TargetQuantity = 30, IsGood = false } // Defects }; _mockDeviceRepository.Setup(repo => repo.GetByIdAsync(1)) .ReturnsAsync(device); _mockDeviceRepository.Setup(repo => repo.GetAllActiveDevicesAsync()) .ReturnsAsync(new List { device }); _mockProductionRepository.Setup(repo => repo.GetProductionRecordsByFilterAsync(It.IsAny())) .ReturnsAsync(records); // Act var result = await _statisticsService.PerformQualityAnalysisAsync(filter); // Assert Assert.NotNull(result); Assert.Single(result.DeviceIds); Assert.Equal(1, result.TotalProduced); Assert.Equal(0.8m, result.QualityRate); // 80% quality rate Assert.Equal(0.2m, result.DefectRate); // 20% defect rate Assert.True(result.QualityMetrics.Count > 0); } } }