You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
haoliang/Haoliang.Tests/ProductionStatisticsService...

376 lines
17 KiB
C#

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<IProductionRepository> _mockProductionRepository;
private readonly Mock<IDeviceRepository> _mockDeviceRepository;
private readonly Mock<ISystemRepository> _mockSystemRepository;
private readonly Mock<IAlarmRepository> _mockAlarmRepository;
private readonly Mock<ICollectionRepository> _mockCollectionRepository;
private readonly ProductionStatisticsService _statisticsService;
public ProductionStatisticsServiceTests()
{
_mockProductionRepository = new Mock<IProductionRepository>();
_mockDeviceRepository = new Mock<IDeviceRepository>();
_mockSystemRepository = new Mock<ISystemRepository>();
_mockAlarmRepository = new Mock<IAlarmRepository>();
_mockCollectionRepository = new Mock<ICollectionRepository>();
_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<ProductionRecord>
{
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<KeyNotFoundException>(() =>
_statisticsService.CalculateProductionTrendsAsync(deviceId, startDate, endDate));
}
[Fact]
public async Task GenerateProductionReportAsync_ValidFilter_ReturnsProductionReport()
{
// Arrange
var filter = new ReportFilter
{
DeviceIds = new List<int> { 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<ProductionRecord>
{
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<DeviceSummary>
{
new DeviceSummary { DeviceId = 1, DeviceName = "Test Device", TodayProduction = 100, Efficiency = 85, QualityRate = 98 }
};
var alertSummaries = new List<AlertSummary>
{
new AlertSummary { AlertId = 1, DeviceName = "Test Device", AlertType = AlertType.DeviceOffline, Message = "Device offline" }
};
_mockDeviceRepository.Setup(repo => repo.GetAllDevicesAsync())
.ReturnsAsync(new List<CNCDevice> { 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<ProductionRecord>());
_mockAlarmRepository.Setup(repo => GetActiveAlertsByDeviceAsync(1))
.ReturnsAsync(new List<Alert> { 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<ProductionRecord>
{
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<DeviceStatusHistory>());
// 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<ProductionRecord>
{
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<int> { 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<ProductionRecord>
{
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<int> { 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<ProductionRecord>
{
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<CNCDevice> { 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<int> { 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<ProductionRecord>
{
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<CNCDevice> { device });
_mockProductionRepository.Setup(repo => repo.GetProductionRecordsByFilterAsync(It.IsAny<ReportFilter>()))
.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);
}
}
}