using System; using System.Collections.Generic; using Xunit; using CncModels.Dto.Dashboard; using CncModels.Entity; using CncRepository.Interface; using CncService.Interface; using CncService.Impl; namespace CncService.Tests { // Fake repositories to isolate DashboardService.GetCollectorStatus tests public class FakeDashboardRepository : IDashboardRepository { public DashboardSummaryResponse GetSummary() => new DashboardSummaryResponse(); public List GetWorkshopProduction(DateTime startDate, DateTime endDate) => new List(); public List GetMachineRank(DateTime startDate, DateTime endDate, int top, string sortOrder = "desc") => new List(); public List GetWorkerRank(DateTime startDate, DateTime endDate, int top, string sortOrder = "desc") => new List(); public List GetProductionTrend(int days) => new List(); public object GetMachineStatusDistribution() => new object(); public List GetRecentAlerts(int count) => new List(); public List GetProgramRank(DateTime startDate, DateTime endDate, int top, string sortOrder = "desc") => new List(); public List GetProgramDistribution(DateTime startDate, DateTime endDate, int top = 10) => new List(); } public class FakeCollectorHeartbeatRepository : ICollectorHeartbeatRepository { private readonly CollectorHeartbeat _latest; public FakeCollectorHeartbeatRepository(CollectorHeartbeat latest) { _latest = latest; } public long Create(CollectorHeartbeat entity) => 1; public CollectorHeartbeat GetLatest(string serviceId) => _latest; public int DeleteBeforeDate(DateTime date) => 0; } public class FakeWindowsServiceChecker : IWindowsServiceChecker { private readonly ServiceStatusEnum _status; public FakeWindowsServiceChecker(ServiceStatusEnum status) { _status = status; } public ServiceStatusEnum GetServiceStatus(string serviceName) => _status; public (bool, string) TryStartService(string serviceName, int timeoutSeconds) => ( _status == ServiceStatusEnum.NotInstalled ? false : true, _status == ServiceStatusEnum.NotInstalled ? "NotInstalled" : "Started"); public (bool, string) TryStopService(string serviceName, int timeoutSeconds) => ( true, "Stopped"); } public class FakeSysConfigRepository : ISysConfigRepository { public SysConfig GetByKey(string configKey) => new SysConfig { ConfigKey = configKey, ConfigValue = "300" }; public List GetAll() => new List(); public bool UpdateValue(int id, string value) => true; } [Collection("Database")] public class DashboardServiceTests : IDisposable { private readonly DashboardService _service; public DashboardServiceTests() { TestDb.TruncateAll(); _service = ServiceFactory.CreateDashboardService(); } public void Dispose() { TestDb.TruncateAll(); } [Fact] public void GetCollectorStatus_With_NotInstalled_Service_Returns_NotInstalled_State() { // Arrange var latest = new CollectorHeartbeat { Id = 1, ServiceId = "collector-service", Status = "running", UptimeSeconds = 120, LastCollectTime = DateTime.Now, CreatedAt = DateTime.Now }; var dashboardRepo = new FakeDashboardRepository(); var heartbeatRepo = new FakeCollectorHeartbeatRepository(latest); var checker = new FakeWindowsServiceChecker(CncService.Interface.ServiceStatusEnum.NotInstalled); var svc = new DashboardService(dashboardRepo, heartbeatRepo, new FakeSysConfigRepository(), checker); // Act var resultObj = svc.GetCollectorStatus(); var t = resultObj.GetType(); var statusProp = t.GetProperty("status"); var serviceStatusProp = t.GetProperty("serviceStatus"); var uptimeProp = t.GetProperty("uptimeSeconds"); var lastCollectTimeProp = t.GetProperty("lastCollectTime"); Assert.NotNull(statusProp); Assert.NotNull(serviceStatusProp); var statusVal = statusProp.GetValue(resultObj) as string; var serviceStatusVal = serviceStatusProp.GetValue(resultObj) as string; Assert.Equal("not_installed", statusVal); Assert.Equal("NotInstalled", serviceStatusVal); } [Fact] public void GetCollectorStatus_With_Running_Heartbeats_Returns_Running_State() { var latest = new CollectorHeartbeat { Id = 1, ServiceId = "collector-service", Status = "running", UptimeSeconds = 60, LastCollectTime = DateTime.Now, CreatedAt = DateTime.Now }; var dashboardRepo = new FakeDashboardRepository(); var heartbeatRepo = new FakeCollectorHeartbeatRepository(latest); var checker = new FakeWindowsServiceChecker(CncService.Interface.ServiceStatusEnum.Running); var svc = new DashboardService(dashboardRepo, heartbeatRepo, new FakeSysConfigRepository(), checker); var resultObj = svc.GetCollectorStatus(); var t = resultObj.GetType(); var serviceStatusProp = t.GetProperty("serviceStatus"); var statusProp = t.GetProperty("status"); var serviceStatusVal = serviceStatusProp.GetValue(resultObj) as string; var statusVal = statusProp.GetValue(resultObj) as string; Assert.Equal("Running", serviceStatusVal); Assert.Equal("running", statusVal); } [Fact] public void GetCollectorStatus_With_Starting_ServiceStatus_Returns_Starting_State() { var latest = new CollectorHeartbeat { Id = 2, ServiceId = "collector-service", Status = "running", UptimeSeconds = 120, LastCollectTime = DateTime.Now, CreatedAt = DateTime.Now }; var dashboardRepo = new FakeDashboardRepository(); var heartbeatRepo = new FakeCollectorHeartbeatRepository(latest); var checker = new FakeWindowsServiceChecker(CncService.Interface.ServiceStatusEnum.Starting); var svc = new DashboardService(dashboardRepo, heartbeatRepo, new FakeSysConfigRepository(), checker); var resultObj = svc.GetCollectorStatus(); var t = resultObj.GetType(); var serviceStatusProp = t.GetProperty("serviceStatus"); var statusProp = t.GetProperty("status"); var serviceStatusVal = serviceStatusProp.GetValue(resultObj) as string; var statusVal = statusProp.GetValue(resultObj) as string; Assert.Equal("Starting", serviceStatusVal); Assert.Equal("running", statusVal); } // ======== GetProgramRank ======== [Fact] public void GetProgramRank_无数据_返回空列表() { var result = _service.GetProgramRank(null, null); Assert.NotNull(result); Assert.Empty(result); } [Fact] public void GetProgramRank_有数据_返回排行列表() { // 插入测试数据 TestDb.Execute(@"INSERT INTO cnc_collect_address (name, url, brand_id, collect_interval, is_enabled, created_at, updated_at) VALUES ('测试地址', 'http://test', 1, 30, 1, NOW(), NOW())"); TestDb.Execute(@"INSERT INTO cnc_machine (device_code, name, workshop_id, collect_address_id, ip_address, brand_id, is_enabled, created_at, updated_at) VALUES ('M001', '机床1', 1, 1, '0.0.0.0', 1, 1, NOW(), NOW())"); TestDb.Execute(@"INSERT INTO cnc_machine (device_code, name, workshop_id, collect_address_id, ip_address, brand_id, is_enabled, created_at, updated_at) VALUES ('M002', '机床2', 1, 1, '0.0.0.0', 1, 1, NOW(), NOW())"); TestDb.Execute(@"INSERT INTO cnc_production_segment (machine_id, production_date, program_name, start_time, start_part_count, end_part_count, quantity, is_settled, created_at, updated_at) VALUES (1, CURDATE(), 'O0001', NOW(), 0, 100, 100, 1, NOW(), NOW())"); TestDb.Execute(@"INSERT INTO cnc_production_segment (machine_id, production_date, program_name, start_time, start_part_count, end_part_count, quantity, is_settled, created_at, updated_at) VALUES (2, CURDATE(), 'O0001', NOW(), 0, 50, 50, 1, NOW(), NOW())"); var result = _service.GetProgramRank(DateTime.Today, DateTime.Today); Assert.NotEmpty(result); Assert.Equal("O0001", result[0].ProgramName); Assert.Equal(150, result[0].TotalQuantity); Assert.Equal(2, result[0].MachineCount); Assert.Equal(1, result[0].Rank); } // ======== GetProgramDistribution ======== [Fact] public void GetProgramDistribution_无数据_返回空列表() { var result = _service.GetProgramDistribution(null, null); Assert.NotNull(result); Assert.Empty(result); } [Fact] public void GetProgramDistribution_有数据_返回分布列表() { // 插入测试数据 TestDb.Execute(@"INSERT INTO cnc_collect_address (name, url, brand_id, collect_interval, is_enabled, created_at, updated_at) VALUES ('测试地址', 'http://test', 1, 30, 1, NOW(), NOW())"); TestDb.Execute(@"INSERT INTO cnc_machine (device_code, name, workshop_id, collect_address_id, ip_address, brand_id, is_enabled, created_at, updated_at) VALUES ('M001', '机床1', 1, 1, '0.0.0.0', 1, 1, NOW(), NOW())"); TestDb.Execute(@"INSERT INTO cnc_production_segment (machine_id, production_date, program_name, start_time, start_part_count, end_part_count, quantity, is_settled, created_at, updated_at) VALUES (1, CURDATE(), 'O0001', NOW(), 0, 80, 80, 1, NOW(), NOW())"); TestDb.Execute(@"INSERT INTO cnc_production_segment (machine_id, production_date, program_name, start_time, start_part_count, end_part_count, quantity, is_settled, created_at, updated_at) VALUES (1, CURDATE(), 'O0002', NOW(), 0, 20, 20, 1, NOW(), NOW())"); var result = _service.GetProgramDistribution(DateTime.Today, DateTime.Today); Assert.NotEmpty(result); Assert.Equal(2, result.Count); Assert.Equal("O0001", result[0].ProgramName); Assert.Equal(80, result[0].TotalQuantity); Assert.Equal(80m, result[0].Percentage); Assert.Equal("O0002", result[1].ProgramName); Assert.Equal(20, result[1].TotalQuantity); Assert.Equal(20m, result[1].Percentage); } } }