using System; using Xunit; using CncCollector.Core; namespace CncCollector.Tests { /// /// ProductionTracker 单元测试。 /// ProductionTracker 直接使用 MySqlConnection,无法通过 Moq 注入, /// 因此使用无效连接字符串验证容错行为(不抛异常)。 /// public class ProductionTrackerTests : IDisposable { /// 无效连接字符串,用于触发错误处理路径 private const string InvalidConnStr = "Server=invalid;Database=invalid;User Id=invalid;Password=invalid;"; private ProductionTracker _tracker; public ProductionTrackerTests() { _tracker = new ProductionTracker(InvalidConnStr); } public void Dispose() { _tracker?.Dispose(); } // ===== 构造函数测试 ===== [Fact] public void 构造函数_正常创建() { var tracker = new ProductionTracker(InvalidConnStr); Assert.NotNull(tracker); } [Fact] public void 构造函数_null连接字符串_正常创建() { // 构造函数本身不做连接验证,允许 null var tracker = new ProductionTracker(null); Assert.NotNull(tracker); } // ===== Track 方法测试 ===== [Fact] public void Track_无效DB连接_不抛异常() { // 使用无效连接字符串调用 Track,内部 DB 操作会失败但不应该向外抛出异常 var ex = Record.Exception(() => _tracker.Track(1, "O0001", 100m, DateTime.Now)); Assert.Null(ex); } [Fact] public void Track_空程序名_不抛异常() { // programName 为空时应提前返回,不做任何 DB 操作 var ex = Record.Exception(() => _tracker.Track(1, "", 100m, DateTime.Now)); Assert.Null(ex); } [Fact] public void Track_null程序名_不抛异常() { var ex = Record.Exception(() => _tracker.Track(1, null, 100m, DateTime.Now)); Assert.Null(ex); } [Fact] public void Track_partCount为null_不抛异常() { var ex = Record.Exception(() => _tracker.Track(1, "O0001", null, DateTime.Now)); Assert.Null(ex); } [Fact] public void Track_连续调用不同程序_不抛异常() { // 模拟程序切换场景 var ex1 = Record.Exception(() => _tracker.Track(1, "O0001", 10m, DateTime.Now)); Assert.Null(ex1); var ex2 = Record.Exception(() => _tracker.Track(1, "O0002", 5m, DateTime.Now)); Assert.Null(ex2); } [Fact] public void Track_零件数下降_不抛异常() { // 模拟手动清零场景:同程序下 partCount 下降 var ex1 = Record.Exception(() => _tracker.Track(1, "O0001", 100m, DateTime.Now)); Assert.Null(ex1); var ex2 = Record.Exception(() => _tracker.Track(1, "O0001", 5m, DateTime.Now)); Assert.Null(ex2); } [Fact] public void Track_多台机床并行_不抛异常() { var ex1 = Record.Exception(() => _tracker.Track(1, "O0001", 10m, DateTime.Now)); Assert.Null(ex1); var ex2 = Record.Exception(() => _tracker.Track(2, "O0002", 20m, DateTime.Now)); Assert.Null(ex2); var ex3 = Record.Exception(() => _tracker.Track(3, "O0003", 30m, DateTime.Now)); Assert.Null(ex3); } // ===== CloseActiveSegment 测试 ===== [Fact] public void CloseActiveSegment_无活跃段_不抛异常() { var ex = Record.Exception(() => _tracker.CloseActiveSegment(999, 100m, "program_change", DateTime.Now)); Assert.Null(ex); } [Fact] public void CloseActiveSegment_null参数_不抛异常() { var ex = Record.Exception(() => _tracker.CloseActiveSegment(1, null, "manual_reset", DateTime.Now)); Assert.Null(ex); } // ===== CloseAllActiveSegments 测试 ===== [Fact] public void CloseAllActiveSegments_无活跃段_不抛异常() { var ex = Record.Exception(() => _tracker.CloseAllActiveSegments()); Assert.Null(ex); } [Fact] public void CloseAllActiveSegments_有Track记录_不抛异常() { // 先 Track 产生内存缓存,再 CloseAll _tracker.Track(1, "O0001", 10m, DateTime.Now); var ex = Record.Exception(() => _tracker.CloseAllActiveSegments()); Assert.Null(ex); } // ===== Dispose 测试 ===== [Fact] public void Dispose_正常调用_不抛异常() { var tracker = new ProductionTracker(InvalidConnStr); var ex = Record.Exception(() => tracker.Dispose()); Assert.Null(ex); } [Fact] public void Dispose_多次调用_不抛异常() { var tracker = new ProductionTracker(InvalidConnStr); tracker.Dispose(); tracker.Dispose(); // 二次调用不应抛异常 } } }