diff --git a/tests/CncModels.Tests/ApiResponseTests.cs b/tests/CncModels.Tests/ApiResponseTests.cs new file mode 100644 index 0000000..11fb089 --- /dev/null +++ b/tests/CncModels.Tests/ApiResponseTests.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using Xunit; +using CncModels.Dto; + +namespace CncModels.Tests +{ + /// + /// ApiResponse 单元测试 + /// + public class ApiResponseTests + { + [Fact] + /// + /// 成功响应:Code=0, Message=success, Data=传入值 + /// + public void Success_Returns_Code_0_Message_Success_With_Data() + { + // Arrange + var data = "ok"; + // Act + var resp = ApiResponse.Success(data); + + // Assert + Assert.Equal(0, resp.Code); + Assert.Equal("success", resp.Message); + Assert.Equal(data, resp.Data); + } + + [Fact] + /// + /// 成功响应:自定义信息 + /// + public void Success_With_Custom_Message() + { + var resp = ApiResponse.Success("payload", "custom"); + Assert.Equal(0, resp.Code); + Assert.Equal("custom", resp.Message); + Assert.Equal("payload", resp.Data); + } + + [Fact] + /// + /// 失败响应:非零错误码且数据为 null + /// + public void Fail_Returns_NonZero_Code_And_Null_Data() + { + var resp = ApiResponse.Fail(400, "bad request"); + Assert.Equal(400, resp.Code); + Assert.Equal("bad request", resp.Message); + Assert.Null(resp.Data); + } + + [Fact] + /// + /// 成功响应:数据为 null 的情况 + /// + public void Success_With_Null_Data() + { + var resp = ApiResponse.Success(null); + Assert.Equal(0, resp.Code); + Assert.Equal("success", resp.Message); + Assert.Null(resp.Data); + } + + [Fact] + /// + /// 成功响应:复杂对象数据 + /// + public void Success_With_Complex_Data() + { + var data = new List { 1, 2, 3 }; + var resp = ApiResponse>.Success(data); + Assert.Equal(0, resp.Code); + Assert.Equal("success", resp.Message); + Assert.Equal(data, resp.Data); + } + } +} diff --git a/tests/CncModels.Tests/ConstantsTests.cs b/tests/CncModels.Tests/ConstantsTests.cs new file mode 100644 index 0000000..1fa45d7 --- /dev/null +++ b/tests/CncModels.Tests/ConstantsTests.cs @@ -0,0 +1,101 @@ +using System; +using System.Reflection; +using System.Text; +using Xunit; +using CncModels.Constants; + +namespace CncModels.Tests +{ + /// + /// 常量类测试:TableName、SensitiveConfigKey、ErrorCode 的常量值应符合命名约定与格式要求。 + /// + public class ConstantsTests + { + /// + /// 验证 TableName 的 20 个表名常量值与数据库实际表名一致 + /// 覆盖场景:全部20张表的精确值校验 + /// + [Fact] + public void TableName_AllConstants_MatchDatabaseTableNames() + { + // 业务库 17 张表 + Assert.Equal("cnc_workshop", TableName.Workshop); + Assert.Equal("cnc_brand", TableName.Brand); + Assert.Equal("cnc_brand_field_mapping", TableName.BrandFieldMapping); + Assert.Equal("cnc_collect_address", TableName.CollectAddress); + Assert.Equal("cnc_machine", TableName.Machine); + Assert.Equal("cnc_worker", TableName.Worker); + Assert.Equal("cnc_worker_machine", TableName.WorkerMachine); + Assert.Equal("cnc_collect_record", TableName.CollectRecord); + Assert.Equal("cnc_production_segment", TableName.ProductionSegment); + Assert.Equal("cnc_machine_daily_status", TableName.MachineDailyStatus); + Assert.Equal("cnc_daily_production", TableName.DailyProduction); + Assert.Equal("cnc_worker_daily_summary", TableName.WorkerDailySummary); + Assert.Equal("cnc_production_adjustment", TableName.ProductionAdjustment); + Assert.Equal("cnc_alert", TableName.Alert); + Assert.Equal("cnc_sys_config", TableName.SysConfig); + Assert.Equal("cnc_screen_config", TableName.ScreenConfig); + Assert.Equal("cnc_screen_filter", TableName.ScreenFilter); + // 日志库 3 张表 + Assert.Equal("log_collect_raw", TableName.LogCollectRaw); + Assert.Equal("log_system", TableName.LogSystem); + Assert.Equal("log_collector_heartbeat", TableName.LogCollectorHeartbeat); + } + + /// + /// AAA:验证 SensitiveConfigKey 的 3 个敏感键常量值应为 snake_case 风格。 + /// + [Fact] + public void SensitiveConfigKey_Constants_ShouldFollowSnakeCase() + { + Type t = typeof(SensitiveConfigKey); + var fields = t.GetFields(BindingFlags.Public | BindingFlags.Static); + Assert.Equal(3, fields.Length); + foreach (var f in fields) + { + if (f.FieldType != typeof(string)) continue; + string value = (string)f.GetValue(null); + string expected = ToSnakeCase(f.Name); + Assert.Equal(expected, value); + } + } + + /// + /// AAA:验证 ErrorCode 的 6 个错误码常量值应为纯数字字符串。 + /// + [Fact] + public void ErrorCode_Constants_ShouldBeNumericStrings() + { + Type t = typeof(ErrorCode); + var fields = t.GetFields(BindingFlags.Public | BindingFlags.Static); + // 至少应有 6 个错误码常量,若有更多也应遵循数字字符串规则 + Assert.True(fields.Length >= 6, "ErrorCode 至少应包含 6 个常量"); + foreach (var f in fields) + { + if (f.FieldType != typeof(string)) continue; + string value = (string)f.GetValue(null); + Assert.Matches("^[0-9]+$", value); + } + } + + private string ToSnakeCase(string input) + { + if (string.IsNullOrEmpty(input)) return input; + var sb = new StringBuilder(); + for (int i = 0; i < input.Length; i++) + { + char c = input[i]; + if (char.IsUpper(c)) + { + if (i > 0) sb.Append('_'); + sb.Append(char.ToLowerInvariant(c)); + } + else + { + sb.Append(c); + } + } + return sb.ToString(); + } + } +} diff --git a/tests/CncModels.Tests/DtoTests.cs b/tests/CncModels.Tests/DtoTests.cs new file mode 100644 index 0000000..3756de7 --- /dev/null +++ b/tests/CncModels.Tests/DtoTests.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using Xunit; +using CncModels.Dto.Login; +using CncModels.Dto.Machine; + +namespace CncModels.Tests +{ + /// + /// DTO 属性赋值测试(对关键 DTO 的简单结构进行读写回填验证) + /// + public class DtoTests + { + [Fact] + /// + /// LoginRequest 属性赋值读取回填 + /// + public void LoginRequest_Property_Assignment() + { + // Arrange + var req = new LoginRequest + { + Username = "user1", + Password = "pwd", + RememberMe = true + }; + + // Assert + Assert.Equal("user1", req.Username); + Assert.Equal("pwd", req.Password); + Assert.True(req.RememberMe); + } + + [Fact] + /// + /// MachineQuery 继承自 PagedQuery 且字段赋值正确 + /// + public void MachineQuery_Inherits_PagedQuery_And_Sets_Others() + { + var q = new MachineQuery + { + Page = 2, + PageSize = 5, + Keyword = "lathe", + WorkshopId = 3, + IsOnline = 1, + BrandId = 7 + }; + + Assert.Equal(2, q.Page); + Assert.Equal(5, q.PageSize); + Assert.Equal("lathe", q.Keyword); + Assert.Equal(3, q.WorkshopId); + Assert.Equal(1, q.IsOnline); + Assert.Equal(7, q.BrandId); + Assert.Equal(5, q.Offset); // (2-1)*5 + } + + [Fact] + /// + /// CreateMachineRequest 属性赋值读取回填 + /// + public void CreateMachineRequest_Property_Assignment() + { + var req = new CreateMachineRequest + { + DeviceCode = "D1", + Name = "Machine 1", + WorkshopId = 2, + CollectAddressId = 3, + IpAddress = "192.168.1.10", + BrandId = 8 + }; + + Assert.Equal("D1", req.DeviceCode); + Assert.Equal("Machine 1", req.Name); + Assert.Equal(2, req.WorkshopId); + Assert.Equal(3, req.CollectAddressId); + Assert.Equal("192.168.1.10", req.IpAddress); + Assert.Equal(8, req.BrandId); + } + + [Fact] + /// + /// MachineListItem 属性赋值读取回填 + /// + public void MachineListItem_Property_Assignment() + { + var item = new MachineListItem + { + Id = 1, + DeviceCode = "M1", + Name = "Machine 1", + WorkshopId = 2, + WorkshopName = "Workshop A", + CollectAddressId = 4, + BrandId = 8, + BrandName = "BrandX", + IpAddress = "10.0.0.1", + IsEnabled = 1, + IsOnline = 1, + WorkerId = 9, + WorkerName = "Worker1", + LastProgramName = "P1", + LastCollectTime = new DateTime(2026, 4, 1) + }; + + Assert.Equal(1, item.Id); + Assert.Equal("M1", item.DeviceCode); + Assert.Equal("Machine 1", item.Name); + Assert.Equal(2, item.WorkshopId); + Assert.Equal("Workshop A", item.WorkshopName); + Assert.Equal(4, item.CollectAddressId); + Assert.Equal(8, item.BrandId); + Assert.Equal("BrandX", item.BrandName); + Assert.Equal("10.0.0.1", item.IpAddress); + Assert.Equal(1, item.IsEnabled); + Assert.Equal(1, item.IsOnline); + Assert.Equal(9, item.WorkerId); + Assert.Equal("Worker1", item.WorkerName); + Assert.Equal("P1", item.LastProgramName); + Assert.Equal(new DateTime(2026, 4, 1), item.LastCollectTime); + } + } +} diff --git a/tests/CncModels.Tests/EntityTests.cs b/tests/CncModels.Tests/EntityTests.cs new file mode 100644 index 0000000..6d9f401 --- /dev/null +++ b/tests/CncModels.Tests/EntityTests.cs @@ -0,0 +1,775 @@ +using System; +using CncModels.Entity; +using Xunit; + +namespace CncModels.Tests +{ + public class EntityTests + { + // Workshop + [Fact] + public void Workshop_DefaultsAndAssignments_ReturnsCorrectValues() + { + // Arrange: defaults + var w0 = new Workshop(); + Assert.Equal(0, w0.Id); + Assert.Null(w0.Name); + Assert.Equal(0, w0.SortOrder); + Assert.Equal(0, w0.IsEnabled); + Assert.Equal(default(DateTime), w0.CreatedAt); + Assert.Equal(default(DateTime), w0.UpdatedAt); + + // Act: assign all fields + var w = new Workshop + { + Id = 1, + Name = "A栋", + SortOrder = 2, + IsEnabled = 1, + CreatedAt = new DateTime(2026, 4, 28), + UpdatedAt = new DateTime(2026, 4, 29) + }; + + // Assert: values + Assert.Equal(1, w.Id); + Assert.Equal("A栋", w.Name); + Assert.Equal(2, w.SortOrder); + Assert.Equal(1, w.IsEnabled); + Assert.Equal(new DateTime(2026, 4, 28), w.CreatedAt); + Assert.Equal(new DateTime(2026, 4, 29), w.UpdatedAt); + } + + // Brand + [Fact] + public void Brand_DefaultsAndAssignments_ReturnsCorrectValues() + { + var b0 = new Brand(); + Assert.Equal(0, b0.Id); + Assert.Null(b0.BrandName); + Assert.Null(b0.DeviceField); + Assert.Null(b0.TagsPath); + Assert.Equal(0, b0.IsEnabled); + Assert.Equal(default(DateTime), b0.CreatedAt); + Assert.Equal(default(DateTime), b0.UpdatedAt); + + var b = new Brand + { + Id = 3, + BrandName = "BrandX", + DeviceField = "fieldX", + TagsPath = "/tags/x", + IsEnabled = 1, + CreatedAt = new DateTime(2026, 4, 28), + UpdatedAt = new DateTime(2026, 4, 29) + }; + Assert.Equal(3, b.Id); + Assert.Equal("BrandX", b.BrandName); + Assert.Equal("fieldX", b.DeviceField); + Assert.Equal("/tags/x", b.TagsPath); + Assert.Equal(1, b.IsEnabled); + Assert.Equal(new DateTime(2026, 4, 28), b.CreatedAt); + Assert.Equal(new DateTime(2026, 4, 29), b.UpdatedAt); + } + + // BrandFieldMapping + [Fact] + public void BrandFieldMapping_DefaultsAndAssignments_ReturnsCorrectValues() + { + var m0 = new BrandFieldMapping(); + Assert.Equal(0, m0.Id); + Assert.Equal(0, m0.BrandId); + Assert.Null(m0.StandardField); + Assert.Null(m0.FieldName); + Assert.Null(m0.MatchBy); + Assert.Null(m0.DataType); + Assert.Equal(0, m0.IsRequired); + Assert.Equal(default(DateTime), m0.CreatedAt); + + var m = new BrandFieldMapping + { + Id = 1, + BrandId = 2, + StandardField = "Model", + FieldName = "mdl", + MatchBy = "equals", + DataType = "string", + IsRequired = 1, + CreatedAt = new DateTime(2026, 4, 28) + }; + Assert.Equal(1, m.Id); + Assert.Equal(2, m.BrandId); + Assert.Equal("Model", m.StandardField); + Assert.Equal("mdl", m.FieldName); + Assert.Equal("equals", m.MatchBy); + Assert.Equal("string", m.DataType); + Assert.Equal(1, m.IsRequired); + Assert.Equal(new DateTime(2026, 4, 28), m.CreatedAt); + } + + // CollectAddress + [Fact] + public void CollectAddress_DefaultsAndAssignments_ReturnsCorrectValues() + { + var a0 = new CollectAddress(); + Assert.Equal(0, a0.Id); + Assert.Null(a0.Name); + Assert.Null(a0.Url); + Assert.Equal(0, a0.BrandId); + Assert.Equal(0, a0.CollectInterval); + Assert.Equal(0, a0.IsEnabled); + Assert.Null(a0.LastCollectTime); + Assert.Null(a0.LastCollectStatus); + Assert.Equal(0, a0.FailCount); + Assert.Equal(default(DateTime), a0.CreatedAt); + Assert.Equal(default(DateTime), a0.UpdatedAt); + + var a = new CollectAddress + { + Id = 10, + Name = "地址1", + Url = "http://127.0.0.1", + BrandId = 5, + CollectInterval = 60, + IsEnabled = 1, + LastCollectTime = new DateTime(2026, 4, 28), + LastCollectStatus = "OK", + FailCount = 0, + CreatedAt = new DateTime(2026, 4, 29), + UpdatedAt = new DateTime(2026, 4, 30) + }; + Assert.Equal(10, a.Id); + Assert.Equal("地址1", a.Name); + Assert.Equal("http://127.0.0.1", a.Url); + Assert.Equal(5, a.BrandId); + Assert.Equal(60, a.CollectInterval); + Assert.Equal(1, a.IsEnabled); + Assert.Equal(new DateTime(2026, 4, 28), a.LastCollectTime); + Assert.Equal("OK", a.LastCollectStatus); + Assert.Equal(0, a.FailCount); + Assert.Equal(new DateTime(2026, 4, 29), a.CreatedAt); + Assert.Equal(new DateTime(2026, 4, 30), a. UpdatedAt); + } + + // CollectRecord + [Fact] + public void CollectRecord_DefaultsAndAssignments_ReturnsCorrectValues() + { + var r0 = new CollectRecord(); + Assert.Equal(0L, r0.Id); + Assert.Equal(default(DateTime), r0.CollectTime); + Assert.Equal(0, r0.MachineId); + Assert.Null(r0.ProgramName); + Assert.Null(r0.DeviceStatus); + Assert.Null(r0.RunStatus); + Assert.Null(r0.OperateMode); + Assert.Null(r0.ExtraData); + + var r = new CollectRecord + { + Id = 1234567890123, + MachineId = 7, + CollectTime = new DateTime(2026, 4, 28, 12, 0, 0), + DeviceTime = new DateTime(2026, 4, 28, 12, 0, 1), + ProgramName = "Prog1", + PartCount = 99.5m, + DeviceStatus = "OK", + RunStatus = "Running", + OperateMode = "Auto", + SpindleSpeedSet = 1500.0m, + FeedSpeedSet = 200.0m, + SpindleSpeedActual = 1499.0m, + FeedSpeedActual = 198.0m, + SpindleLoad = 75.0m, + SpindleOverride = 1.0m, + PowerOnTime = 3600.0m, + RunTime = 7200.0m, + CuttingTime = 1800.0m, + CycleTime = 300.0m, + MachiningStatus = "Machining", + ExtraData = "{}", + }; + // Partial assertions to cover types + Assert.Equal(1234567890123, r.Id); + Assert.Equal(7, r.MachineId); + Assert.Equal(new DateTime(2026, 4, 28, 12, 0, 0), r.CollectTime); + Assert.Equal("Prog1", r.ProgramName); + Assert.Equal(99.5m, r.PartCount); + Assert.Equal("OK", r.DeviceStatus); + Assert.Equal("Running", r.RunStatus); + Assert.Equal(1500.0m, r.SpindleSpeedSet); + Assert.Equal(200.0m, r.FeedSpeedSet); + Assert.Equal("Machining", r.MachiningStatus); + } + + // ProductionSegment + [Fact] + public void ProductionSegment_DefaultsAndAssignments_ReturnsCorrectValues() + { + var s0 = new ProductionSegment(); + Assert.Equal(0L, s0.Id); + Assert.Equal(0, s0.MachineId); + Assert.Null(s0.ProgramName); + Assert.Equal(default(DateTime), s0.ProductionDate); + Assert.Equal(default(DateTime), s0.StartTime); + Assert.Null(s0.EndTime); + Assert.Equal(0m, s0.StartPartCount); + Assert.Null(s0.EndPartCount); + Assert.Null(s0.Quantity); + Assert.Equal(0, s0.IsSettled); + Assert.Null(s0.CloseReason); + Assert.Equal(default(DateTime), s0.CreatedAt); + Assert.Equal(default(DateTime), s0.UpdatedAt); + + var s = new ProductionSegment + { + Id = 1, + MachineId = 2, + ProgramName = "Prog", + ProductionDate = new DateTime(2026, 4, 28), + StartTime = new DateTime(2026, 4, 28, 8, 0, 0), + EndTime = new DateTime(2026, 4, 28, 12, 0, 0), + StartPartCount = 10.0m, + EndPartCount = 20.0m, + Quantity = 10.0m, + IsSettled = 1, + CloseReason = "End", + CreatedAt = new DateTime(2026, 4, 28), + UpdatedAt = new DateTime(2026, 4, 29) + }; + Assert.Equal(1, s.Id); + Assert.Equal(2, s.MachineId); + Assert.Equal("Prog", s.ProgramName); + Assert.Equal(new DateTime(2026, 4, 28), s.ProductionDate); + Assert.Equal(new DateTime(2026, 4, 28, 8, 0, 0), s.StartTime); + Assert.Equal(new DateTime(2026, 4, 28, 12, 0, 0), s.EndTime); + Assert.Equal(10.0m, s.StartPartCount); + Assert.Equal(20.0m, s.EndPartCount); + Assert.Equal(10.0m, s.Quantity); + Assert.Equal(1, s.IsSettled); + Assert.Equal("End", s.CloseReason); + Assert.Equal(new DateTime(2026, 4, 28), s.CreatedAt); + Assert.Equal(new DateTime(2026, 4, 29), s.UpdatedAt); + } + + // MachineDailyStatus + [Fact] + public void MachineDailyStatus_DefaultsAndAssignments_ReturnsCorrectValues() + { + var m0 = new MachineDailyStatus(); + Assert.Equal(0, m0.Id); + Assert.Equal(0, m0.MachineId); + Assert.Null(m0.DataStatus); + Assert.Equal(default(DateTime), m0.CreatedAt); + Assert.Equal(default(DateTime), m0.UpdatedAt); + + var m = new MachineDailyStatus + { + Id = 5, + MachineId = 9, + ProductionDate = new DateTime(2026, 4, 28), + DataStatus = "Online", + CreatedAt = new DateTime(2026, 4, 28), + UpdatedAt = new DateTime(2026, 4, 29) + }; + Assert.Equal(5, m.Id); + Assert.Equal(9, m.MachineId); + Assert.Equal("Online", m.DataStatus); + Assert.Equal(new DateTime(2026, 4, 28), m.ProductionDate); + Assert.Equal(new DateTime(2026, 4, 28), m.CreatedAt); + Assert.Equal(new DateTime(2026, 4, 29), m.UpdatedAt); + } + + // DailyProduction + [Fact] + public void DailyProduction_DefaultsAndAssignments_ReturnsCorrectValues() + { + var d0 = new DailyProduction(); + Assert.Equal(0, d0.Id); + Assert.Equal(0, d0.MachineId); + Assert.Equal(default(DateTime), d0.ProductionDate); + Assert.Null(d0.ProgramName); + Assert.Equal(0m, d0.TotalQuantity); + Assert.Equal(0, d0.SegmentCount); + Assert.Null(d0.TotalRunTime); + Assert.Null(d0.TotalCuttingTime); + Assert.Null(d0.TotalCycleTime); + Assert.Equal(0, d0.IsAdjusted); + Assert.Null(d0.AdjustedQuantity); + Assert.Equal(default(DateTime), d0.CreatedAt); + Assert.Equal(default(DateTime), d0.UpdatedAt); + + var d = new DailyProduction + { + Id = 2, + MachineId = 3, + ProductionDate = new DateTime(2026, 4, 28), + ProgramName = "ProgA", + TotalQuantity = 100.5m, + SegmentCount = 5, + TotalRunTime = 3600.0m, + TotalCuttingTime = 1800.0m, + TotalCycleTime = 1200.0m, + IsAdjusted = 0, + AdjustedQuantity = 0.0m, + CreatedAt = new DateTime(2026, 4, 28), + UpdatedAt = new DateTime(2026, 4, 29) + }; + Assert.Equal(2, d.Id); + Assert.Equal(3, d.MachineId); + Assert.Equal("ProgA", d.ProgramName); + Assert.Equal(new DateTime(2026, 4, 28), d.ProductionDate); + Assert.Equal(100.5m, d.TotalQuantity); + Assert.Equal(5, d.SegmentCount); + Assert.Equal(3600.0m, d.TotalRunTime); + Assert.Equal(1800.0m, d.TotalCuttingTime); + Assert.Equal(1200.0m, d.TotalCycleTime); + Assert.Equal(0, d.IsAdjusted); + Assert.Equal(new DateTime(2026, 4, 28), d.CreatedAt); + Assert.Equal(new DateTime(2026, 4, 29), d.UpdatedAt); + } + + // WorkerDailySummary + [Fact] + public void WorkerDailySummary_DefaultsAndAssignments_ReturnsCorrectValues() + { + var w0 = new WorkerDailySummary(); + Assert.Equal(0, w0.Id); + Assert.Equal(0, w0.WorkerId); + Assert.Equal(default(DateTime), w0.ProductionDate); + Assert.Equal(0m, w0.TotalQuantity); + Assert.Equal(0, w0.MachineCount); + Assert.Equal(0, w0.ProgramCount); + Assert.Equal(0, w0.IsAdjusted); + Assert.Null(w0.AdjustedQuantity); + Assert.Equal(default(DateTime), w0.CreatedAt); + Assert.Equal(default(DateTime), w0.UpdatedAt); + + var w = new WorkerDailySummary + { + Id = 7, + WorkerId = 8, + ProductionDate = new DateTime(2026, 4, 28), + TotalQuantity = 45.0m, + MachineCount = 2, + ProgramCount = 3, + IsAdjusted = 1, + AdjustedQuantity = 44.5m, + CreatedAt = new DateTime(2026, 4, 28), + UpdatedAt = new DateTime(2026, 4, 29) + }; + Assert.Equal(7, w.Id); + Assert.Equal(8, w.WorkerId); + Assert.Equal(new DateTime(2026, 4, 28), w.ProductionDate); + Assert.Equal(45.0m, w.TotalQuantity); + Assert.Equal(2, w.MachineCount); + Assert.Equal(3, w.ProgramCount); + Assert.Equal(1, w.IsAdjusted); + Assert.Equal(44.5m, w.AdjustedQuantity); + Assert.Equal(new DateTime(2026, 4, 28), w.CreatedAt); + Assert.Equal(new DateTime(2026, 4, 29), w.UpdatedAt); + } + + // ProductionAdjustment + [Fact] + public void ProductionAdjustment_DefaultsAndAssignments_ReturnsCorrectValues() + { + var a0 = new ProductionAdjustment(); + Assert.Equal(0L, a0.Id); + Assert.Null(a0.TargetTable); + Assert.Equal(0, a0.TargetId); + Assert.Null(a0.FieldName); + Assert.Null(a0.OldValue); + Assert.Equal(0m, a0.NewValue); + Assert.Null(a0.Reason); + Assert.Null(a0.OperatorIp); + Assert.Equal(default(DateTime), a0.CreatedAt); + + var a = new ProductionAdjustment + { + Id = 1, + TargetTable = "cnc_daily_production", + TargetId = 2, + FieldName = "TotalQuantity", + OldValue = 10.0m, + NewValue = 12.5m, + Reason = "Adjustment", + OperatorIp = "127.0.0.1", + CreatedAt = new DateTime(2026, 4, 28) + }; + Assert.Equal(1, a.Id); + Assert.Equal("cnc_daily_production", a.TargetTable); + Assert.Equal(2, a.TargetId); + Assert.Equal("TotalQuantity", a.FieldName); + Assert.Equal(10.0m, a.OldValue); + Assert.Equal(12.5m, a.NewValue); + Assert.Equal("Adjustment", a.Reason); + Assert.Equal("127.0.0.1", a.OperatorIp); + Assert.Equal(new DateTime(2026, 4, 28), a.CreatedAt); + } + + // Alert + [Fact] + public void Alert_DefaultsAndAssignments_ReturnsCorrectValues() + { + var al0 = new Alert(); + Assert.Equal(0L, al0.Id); + Assert.Null(al0.AlertType); + Assert.Null(al0.Title); + Assert.Null(al0.Detail); + Assert.Equal(0, al0.IsResolved); + Assert.Equal(default(DateTime), al0.CreatedAt); + Assert.Null(al0.ResolvedAt); + + var al = new Alert + { + Id = 1, + AlertType = "Error", + MachineId = 5, + CollectAddressId = 6, + Title = "Alarm", + Detail = "Detail", + IsResolved = 1, + ResolvedAt = new DateTime(2026, 4, 28), + CreatedAt = new DateTime(2026, 4, 28) + }; + Assert.Equal(1, al.Id); + Assert.Equal("Error", al.AlertType); + Assert.Equal(5, al.MachineId); + Assert.Equal(6, al.CollectAddressId); + Assert.Equal("Alarm", al.Title); + Assert.Equal("Detail", al.Detail); + Assert.Equal(1, al.IsResolved); + Assert.Equal(new DateTime(2026, 4, 28), al.ResolvedAt); + Assert.Equal(new DateTime(2026, 4, 28), al.CreatedAt); + } + + // SysConfig + [Fact] + public void SysConfig_DefaultsAndAssignments_ReturnsCorrectValues() + { + var s0 = new SysConfig(); + Assert.Equal(0, s0.Id); + Assert.Null(s0.ConfigKey); + Assert.Null(s0.ConfigValue); + Assert.Null(s0.ValueType); + Assert.Null(s0.Description); + Assert.Equal(default(DateTime), s0.UpdatedAt); + + var s = new SysConfig + { + Id = 1, + ConfigKey = "MaxThreads", + ConfigValue = "4", + ValueType = "int", + Description = "最大线程数", + UpdatedAt = new DateTime(2026, 4, 29) + }; + Assert.Equal(1, s.Id); + Assert.Equal("MaxThreads", s.ConfigKey); + Assert.Equal("4", s.ConfigValue); + Assert.Equal("int", s.ValueType); + Assert.Equal("最大线程数", s.Description); + Assert.Equal(new DateTime(2026, 4, 29), s.UpdatedAt); + } + + // ScreenConfig + [Fact] + public void ScreenConfig_DefaultsAndAssignments_ReturnsCorrectValues() + { + var sc0 = new ScreenConfig(); + Assert.Equal(0, sc0.Id); + Assert.Null(sc0.CardKey); + Assert.Null(sc0.ChartConfig); + Assert.Equal(default(DateTime), sc0.CreatedAt); + Assert.Equal(default(DateTime), sc0.UpdatedAt); + + var sc = new ScreenConfig + { + Id = 2, + CardKey = "card-1", + CardType = "typeA", + Title = "Title", + Metric = "metric", + Dimension = "dim", + SortOrder = 1, + IsEnabled = 1, + ChartConfig = "{...}", + CreatedAt = new DateTime(2026, 4, 28), + UpdatedAt = new DateTime(2026, 4, 29) + }; + Assert.Equal(2, sc.Id); + Assert.Equal("card-1", sc.CardKey); + Assert.Equal("typeA", sc.CardType); + Assert.Equal("Title", sc.Title); + Assert.Equal("metric", sc.Metric); + Assert.Equal("dim", sc.Dimension); + Assert.Equal(1, sc.SortOrder); + Assert.Equal(1, sc.IsEnabled); + Assert.Equal("{...}", sc.ChartConfig); + Assert.Equal(new DateTime(2026, 4, 28), sc.CreatedAt); + Assert.Equal(new DateTime(2026, 4, 29), sc.UpdatedAt); + } + + // ScreenFilter + [Fact] + public void ScreenFilter_DefaultsAndAssignments_ReturnsCorrectValues() + { + var sf0 = new ScreenFilter(); + Assert.Equal(0, sf0.Id); + Assert.Null(sf0.ScreenKey); + Assert.Null(sf0.FilterType); + Assert.Null(sf0.FilterValue); + Assert.Equal(0, sf0.IsDefault); + Assert.Equal(0, sf0.SortOrder); + + var sf = new ScreenFilter + { + Id = 3, + ScreenKey = "key1", + FilterType = "type", + FilterValue = "value", + IsDefault = 1, + SortOrder = 2 + }; + Assert.Equal(3, sf.Id); + Assert.Equal("key1", sf.ScreenKey); + Assert.Equal("type", sf.FilterType); + Assert.Equal("value", sf.FilterValue); + Assert.Equal(1, sf.IsDefault); + Assert.Equal(2, sf.SortOrder); + } + + // CollectRaw + [Fact] + public void CollectRaw_DefaultsAndAssignments_ReturnsCorrectValues() + { + var cr0 = new CollectRaw(); + Assert.Equal(0L, cr0.Id); + Assert.Equal(0, cr0.CollectAddressId); + Assert.Equal(default(DateTime), cr0.RequestTime); + Assert.Equal(default(DateTime), cr0.ResponseTime); + Assert.Null(cr0.ResponseDuration); + Assert.Equal(0, cr0.IsSuccess); + Assert.Null(cr0.StatusCode); + Assert.Null(cr0.RawJson); + Assert.Null(cr0.ErrorMessage); + Assert.Equal(default(DateTime), cr0.CreatedAt); + + var cr = new CollectRaw + { + Id = 1, + CollectAddressId = 2, + RequestTime = new DateTime(2026, 4, 28, 10, 0, 0), + ResponseTime = new DateTime(2026, 4, 28, 10, 0, 1), + ResponseDuration = 100, + IsSuccess = 1, + StatusCode = 200, + RawJson = "{\"a\":1}", + ErrorMessage = null, + CreatedAt = new DateTime(2026, 4, 28) + }; + Assert.Equal(1, cr.Id); + Assert.Equal(2, cr.CollectAddressId); + Assert.Equal(new DateTime(2026, 4, 28, 10, 0, 0), cr.RequestTime); + Assert.Equal(new DateTime(2026, 4, 28, 10, 0, 1), cr.ResponseTime); + Assert.Equal(100, cr.ResponseDuration); + Assert.Equal(1, cr.IsSuccess); + Assert.Equal(200, cr.StatusCode); + Assert.Equal("{\"a\":1}", cr.RawJson); + Assert.Null(cr.ErrorMessage); + Assert.Equal(new DateTime(2026, 4, 28), cr.CreatedAt); + } + + // SystemLog + [Fact] + public void SystemLog_DefaultsAndAssignments_ReturnsCorrectValues() + { + var sl0 = new SystemLog(); + Assert.Equal(0L, sl0.Id); + Assert.Null(sl0.LogLevel); + Assert.Null(sl0.Source); + Assert.Null(sl0.Message); + Assert.Null(sl0.StackTrace); + Assert.Null(sl0.ExtraData); + Assert.Equal(default(DateTime), sl0.CreatedAt); + + var sl = new SystemLog + { + Id = 9, + LogLevel = "Info", + Source = "SourceA", + Message = "msg", + StackTrace = "trace", + ExtraData = "{}", + CreatedAt = new DateTime(2026, 4, 28) + }; + Assert.Equal(9, sl.Id); + Assert.Equal("Info", sl.LogLevel); + Assert.Equal("SourceA", sl.Source); + Assert.Equal("msg", sl.Message); + Assert.Equal("trace", sl.StackTrace); + Assert.Equal("{}", sl.ExtraData); + Assert.Equal(new DateTime(2026, 4, 28), sl.CreatedAt); + } + + // CollectorHeartbeat + [Fact] + public void CollectorHeartbeat_DefaultsAndAssignments_ReturnsCorrectValues() + { + var cb0 = new CollectorHeartbeat(); + Assert.Equal(0L, cb0.Id); + Assert.Null(cb0.ServiceId); + Assert.Null(cb0.Status); + Assert.Null(cb0.Detail); + Assert.Null(cb0.LastCollectTime); + Assert.Equal(0, cb0.SuccessCount); + Assert.Equal(0, cb0.FailCount); + Assert.Null(cb0.UptimeSeconds); + Assert.Equal(default(DateTime), cb0.CreatedAt); + + var cb = new CollectorHeartbeat + { + Id = 11, + ServiceId = "svc-1", + Status = "Healthy", + CollectAddressId = 4, + LastCollectTime = new DateTime(2026, 4, 28, 12, 0, 0), + SuccessCount = 10, + FailCount = 1, + UptimeSeconds = 3600, + Detail = "{}", + CreatedAt = new DateTime(2026, 4, 28) + }; + Assert.Equal(11, cb.Id); + Assert.Equal("svc-1", cb.ServiceId); + Assert.Equal("Healthy", cb.Status); + Assert.Equal(4, cb.CollectAddressId); + Assert.Equal(new DateTime(2026, 4, 28, 12, 0, 0), cb.LastCollectTime); + Assert.Equal(10, cb.SuccessCount); + Assert.Equal(1, cb.FailCount); + Assert.Equal(3600, cb.UptimeSeconds); + Assert.Equal("{}", cb.Detail); + Assert.Equal(new DateTime(2026, 4, 28), cb.CreatedAt); + } + + // Machine + [Fact] + public void Machine_DefaultsAndAssignments_ReturnsCorrectValues() + { + var m0 = new Machine(); + Assert.Equal(0, m0.Id); + Assert.Null(m0.DeviceCode); + Assert.Null(m0.Name); + Assert.Equal(0, m0.WorkshopId); + Assert.Equal(0, m0.CollectAddressId); + Assert.Null(m0.IpAddress); + Assert.Equal(0, m0.BrandId); + Assert.Equal(0, m0.IsEnabled); + Assert.Equal(0, m0.IsOnline); + Assert.Null(m0.LastPingTime); + Assert.Null(m0.LastCollectTime); + Assert.Null(m0.LastDeviceStatus); + Assert.Null(m0.LastRunStatus); + Assert.Null(m0.LastProgramName); + Assert.Null(m0.LastPartCount); + Assert.Null(m0.LastOperateMode); + Assert.Null(m0.LastMachiningStatus); + Assert.Equal(default(DateTime), m0.CreatedAt); + Assert.Equal(default(DateTime), m0.UpdatedAt); + + var m = new Machine + { + Id = 10, + DeviceCode = "M-10", + Name = "Machine10", + WorkshopId = 1, + CollectAddressId = 2, + IpAddress = "192.168.0.10", + BrandId = 3, + IsEnabled = 1, + IsOnline = 1, + LastPingTime = new DateTime(2026, 4, 28, 12, 0, 0), + LastCollectTime = new DateTime(2026, 4, 28, 12, 5, 0), + LastDeviceStatus = "OK", + LastRunStatus = "Running", + LastProgramName = "Prog1", + LastPartCount = 42.0m, + LastOperateMode = "Auto", + LastMachiningStatus = "Active", + CreatedAt = new DateTime(2026, 4, 28), + UpdatedAt = new DateTime(2026, 4, 29) + }; + Assert.Equal(10, m.Id); + Assert.Equal("M-10", m.DeviceCode); + Assert.Equal("Machine10", m.Name); + Assert.Equal(1, m.WorkshopId); + Assert.Equal(2, m.CollectAddressId); + Assert.Equal("192.168.0.10", m.IpAddress); + Assert.Equal(3, m.BrandId); + Assert.Equal(1, m.IsEnabled); + Assert.Equal(1, m.IsOnline); + Assert.Equal(new DateTime(2026, 4, 28, 12, 0, 0), m.LastPingTime); + Assert.Equal(new DateTime(2026, 4, 28, 12, 5, 0), m.LastCollectTime); + Assert.Equal("OK", m.LastDeviceStatus); + Assert.Equal("Running", m.LastRunStatus); + Assert.Equal("Prog1", m.LastProgramName); + Assert.Equal(42.0m, m.LastPartCount); + Assert.Equal("Auto", m.LastOperateMode); + Assert.Equal("Active", m.LastMachiningStatus); + Assert.Equal(new DateTime(2026, 4, 28), m.CreatedAt); + Assert.Equal(new DateTime(2026, 4, 29), m.UpdatedAt); + } + + // Worker + [Fact] + public void Worker_DefaultsAndAssignments_ReturnsCorrectValues() + { + var w0 = new Worker(); + Assert.Equal(0, w0.Id); + Assert.Null(w0.Name); + Assert.Null(w0.Code); + Assert.Equal(0, w0.IsEnabled); + Assert.Equal(default(DateTime), w0.CreatedAt); + Assert.Equal(default(DateTime), w0.UpdatedAt); + + var w = new Worker + { + Id = 2, + Name = "张三", + Code = "W-2", + IsEnabled = 1, + CreatedAt = new DateTime(2026, 4, 28), + UpdatedAt = new DateTime(2026, 4, 29) + }; + Assert.Equal(2, w.Id); + Assert.Equal("张三", w.Name); + Assert.Equal("W-2", w.Code); + Assert.Equal(1, w.IsEnabled); + Assert.Equal(new DateTime(2026, 4, 28), w.CreatedAt); + Assert.Equal(new DateTime(2026, 4, 29), w.UpdatedAt); + } + + // WorkerMachine + [Fact] + public void WorkerMachine_DefaultsAndAssignments_ReturnsCorrectValues() + { + var wm0 = new WorkerMachine(); + Assert.Equal(0, wm0.Id); + Assert.Equal(0, wm0.WorkerId); + Assert.Equal(0, wm0.MachineId); + Assert.Equal(default(DateTime), wm0.CreatedAt); + + var wm = new WorkerMachine + { + Id = 1, + WorkerId = 2, + MachineId = 3, + CreatedAt = new DateTime(2026, 4, 28) + }; + Assert.Equal(1, wm.Id); + Assert.Equal(2, wm.WorkerId); + Assert.Equal(3, wm.MachineId); + Assert.Equal(new DateTime(2026, 4, 28), wm.CreatedAt); + } + } +} diff --git a/tests/CncModels.Tests/EnumTests.cs b/tests/CncModels.Tests/EnumTests.cs new file mode 100644 index 0000000..d854c66 --- /dev/null +++ b/tests/CncModels.Tests/EnumTests.cs @@ -0,0 +1,83 @@ +using System; +using System.Reflection; +using System.Text; +using Xunit; +using CncModels.Enum; + +namespace CncModels.Tests +{ + /// + /// 测试所有 CncModels.Enum 下的字符串常量,确保值与命名约定一致。 + /// 约定:字段名转换为 snake_case 小写字符串后应当与常量值一致。 + /// 例如 AlertType.CollectFail 应为 "collect_fail"。 + /// + public class EnumTests + { + /// + /// 遍历除 LogLevel 外的所有枚举类,验证 snake_case 命名约定 + /// 覆盖场景:AlertType/CollectStatus/DataStatus/CardType/DataType/MatchBy/SegmentCloseReason/CollectorServiceStatus/ValueType + /// + [Fact] + public void SnakeCaseEnums_ShouldMatchFieldNames() + { + Type[] snakeCaseEnums = new Type[] + { + typeof(AlertType), + typeof(CollectStatus), + typeof(DataStatus), + typeof(CardType), + typeof(DataType), + typeof(MatchBy), + typeof(SegmentCloseReason), + typeof(CollectorServiceStatus), + typeof(CncModels.Enum.ValueType) + }; + + foreach (var t in snakeCaseEnums) + { + var fields = t.GetFields(BindingFlags.Public | BindingFlags.Static); + Assert.True(fields.Length > 0, $"类型 {t.Name} 不包含公共静态字段。"); + foreach (var f in fields) + { + if (f.FieldType != typeof(string)) continue; + string value = (string)f.GetValue(null); + string expected = ToSnakeCase(f.Name); + Assert.Equal(expected, value); + } + } + } + + /// + /// 验证 LogLevel 枚举值为大写(数据库中 log_level 存储为大写) + /// 覆盖场景:DEBUG/INFO/WARN/ERROR 全部值 + /// + [Fact] + public void LogLevel_ShouldBeUppercase() + { + Assert.Equal("DEBUG", LogLevel.Debug); + Assert.Equal("INFO", LogLevel.Info); + Assert.Equal("WARN", LogLevel.Warn); + Assert.Equal("ERROR", LogLevel.Error); + } + + private string ToSnakeCase(string input) + { + if (string.IsNullOrEmpty(input)) return input; + var sb = new StringBuilder(); + for (int i = 0; i < input.Length; i++) + { + char c = input[i]; + if (char.IsUpper(c)) + { + if (i > 0) sb.Append('_'); + sb.Append(char.ToLowerInvariant(c)); + } + else + { + sb.Append(c); + } + } + return sb.ToString(); + } + } +} diff --git a/tests/CncModels.Tests/PagedQueryTests.cs b/tests/CncModels.Tests/PagedQueryTests.cs new file mode 100644 index 0000000..0ea54cb --- /dev/null +++ b/tests/CncModels.Tests/PagedQueryTests.cs @@ -0,0 +1,103 @@ +using System; +using Xunit; +using CncModels.Dto; +using CncModels.Dto.Login; +using CncModels.Dto.Machine; + +namespace CncModels.Tests +{ + /// + /// PagedQuery 单元测试:覆盖默认值、偏移计算与自动修正逻辑 + /// + public class PagedQueryTests + { + [Fact] + /// + /// 默认值正确:page=1, pageSize=20, offset=0 + /// + public void Page_Defaults_Are_Correct() + { + // Arrange/Act + var q = new PagedQuery(); + + // Assert + Assert.Equal(1, q.Page); + Assert.Equal(20, q.PageSize); + Assert.Equal(0, q.Offset); + } + + [Fact] + /// + /// 标准赋值:Page=3, PageSize=10 -> Offset=20 + /// + public void Page_And_PageSize_Normal_Assignments() + { + // Arrange + var q = new PagedQuery + { + Page = 3, + PageSize = 10 + }; + + // Assert + Assert.Equal(3, q.Page); + Assert.Equal(10, q.PageSize); + Assert.Equal(20, q.Offset); + } + + [Fact] + /// + /// 边界:Page=100, PageSize=20 + /// + public void Page_Boundary_Normal() + { + var q = new PagedQuery { Page = 100, PageSize = 20 }; + Assert.Equal(100, q.Page); + Assert.Equal(20, q.PageSize); + Assert.Equal(1980, q.Offset); + } + + [Fact] + /// + /// 自动修正:Page=0 -> 1 + /// + public void Page_AutoFix_When_Zero() + { + var q = new PagedQuery { Page = 0 }; + Assert.Equal(1, q.Page); + } + + [Fact] + /// + /// 自动修正:Page=-5 -> 1 + /// + public void Page_AutoFix_When_Negative() + { + var q = new PagedQuery { Page = -5 }; + Assert.Equal(1, q.Page); + } + + [Fact] + /// + /// 自动修正:PageSize=0 -> 20 + /// + public void PageSize_AutoFix_When_Zero() + { + var q = new PagedQuery { PageSize = 0 }; + Assert.Equal(20, q.PageSize); + } + + [Fact] + /// + /// 自动修正:PageSize=101 -> 100 + /// + public void PageSize_AutoFix_When_Over_Max() + { + var q1 = new PagedQuery { PageSize = 101 }; + Assert.Equal(100, q1.PageSize); + + var q2 = new PagedQuery { PageSize = 999 }; + Assert.Equal(100, q2.PageSize); + } + } +} diff --git a/tests/CncModels.Tests/PagedResultTests.cs b/tests/CncModels.Tests/PagedResultTests.cs new file mode 100644 index 0000000..15ca01a --- /dev/null +++ b/tests/CncModels.Tests/PagedResultTests.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using Xunit; +using CncModels.Dto; + +namespace CncModels.Tests +{ + /// + /// PagedResult 单元测试 + /// + public class PagedResultTests + { + [Theory] + [InlineData(20, 20, 1)] + [InlineData(21, 20, 2)] + [InlineData(0, 10, 0)] + public void TotalPages_Calculation_Works(int total, int pageSize, int expectedPages) + { + // Arrange + var r = new PagedResult + { + Total = total, + PageSize = pageSize, + Page = 1 + }; + + // Act & Assert + Assert.Equal(expectedPages, r.TotalPages); + } + + [Fact] + /// + /// Items 属性默认应为空列表而非 null + /// + public void Items_Should_Be_Empty_List_By_Default() + { + // Arrange + var r = new PagedResult(); + + // Assert + Assert.NotNull(r.Items); + Assert.Empty(r.Items); + } + + [Fact] + /// + /// 属性赋值正确 + /// + public void Properties_Assigned_Correctly() + { + // Arrange + var items = new List { "a", "b" }; + var r = new PagedResult + { + Items = items, + Total = 2, + Page = 1, + PageSize = 2 + }; + + // Assert + Assert.Equal(items, r.Items); + Assert.Equal(2, r.Total); + Assert.Equal(1, r.Page); + Assert.Equal(2, r.PageSize); + Assert.Equal(1, r.TotalPages); // 2/2 = 1 + } + } +}