using DongfangHydro.Dashboard.Api.Domain; using DongfangHydro.Dashboard.Api.Services; using Xunit; namespace DongfangHydro.Dashboard.Tests; public sealed class ProgressAggregationServiceTests { [Fact] public void Recalculate_WeightsProgressAndBubblesRiskAndDelayToTheOrder() { var order = new ProductionOrder { Id = Guid.NewGuid(), Code = "MO-TEST-001", ProductName = "青峪口尾水单向门机 2×160/10", }; var root = Node(order.Id, "ROOT", null, "order", 30, 0); var completed = Node(order.Id, "P-01", root.Id, "part", 10, 10); var delayed = Node(order.Id, "P-02", root.Id, "part", 20, 10); delayed.Status = "delayed"; delayed.RiskLevel = "critical"; delayed.DelayDays = 3; new ProgressAggregationService().Recalculate(order, [root, completed, delayed]); Assert.Equal(67, root.Progress); Assert.Equal(20, root.CompletedQty); Assert.Equal("delayed", root.Status); Assert.Equal("critical", root.RiskLevel); Assert.Equal(3, root.DelayDays); Assert.Equal(67, order.Progress); Assert.Equal(20, order.CompletedQty); Assert.Equal("delayed", order.Status); Assert.Equal("critical", order.RiskLevel); Assert.Equal(3, order.DelayDays); } [Fact] public void Recalculate_MarksParentDoneWhenAllChildrenAreDone() { var order = new ProductionOrder { Id = Guid.NewGuid(), Code = "MO-TEST-002", ProductName = "门架", }; var root = Node(order.Id, "ROOT", null, "order", 10, 0); var childA = Node(order.Id, "P-01", root.Id, "part", 4, 4); var childB = Node(order.Id, "P-02", root.Id, "part", 6, 6); new ProgressAggregationService().Recalculate(order, [root, childA, childB]); Assert.Equal(100, root.Progress); Assert.Equal("done", root.Status); Assert.Equal("normal", root.RiskLevel); Assert.Equal(100, order.Progress); Assert.Equal("done", order.Status); } [Fact] public void Recalculate_KeepsTheParentsPlannedQuantityWhileUsingChildrenAsWeights() { var order = new ProductionOrder { Id = Guid.NewGuid(), Code = "MO-TEST-003", ProductName = "大车行走机构", }; var root = Node(order.Id, "ROOT", null, "order", 5, 0); var childA = Node(order.Id, "P-01", root.Id, "part", 10, 10); var childB = Node(order.Id, "P-02", root.Id, "part", 20, 10); new ProgressAggregationService().Recalculate(order, [root, childA, childB]); Assert.Equal(5, root.RequiredQty); Assert.Equal(3, root.CompletedQty); Assert.Equal(5, order.RequiredQty); Assert.Equal(3, order.CompletedQty); Assert.Equal(67, order.Progress); } [Theory] [InlineData("blocked", 0, "critical")] [InlineData("delayed", 1, "warning")] [InlineData("in_progress", 3, "critical")] public void Recalculate_BubblesOperationalRiskEvenWhenTheLeafRiskWasNotSet( string childStatus, int delayDays, string expectedRisk) { var order = new ProductionOrder { Id = Guid.NewGuid(), Code = "MO-TEST-004", ProductName = "门机电气控制系统", }; var root = Node(order.Id, "ROOT", null, "order", 10, 0); var child = Node(order.Id, "OP-10", root.Id, "process", 10, 4); child.Status = childStatus; child.DelayDays = delayDays; child.RiskLevel = "normal"; new ProgressAggregationService().Recalculate(order, [root, child]); Assert.Equal(expectedRisk, root.RiskLevel); Assert.Equal(expectedRisk, order.RiskLevel); } private static ProductionNode Node( Guid orderId, string code, Guid? parentId, string nodeType, int requiredQty, int completedQty) { return new ProductionNode { Id = Guid.NewGuid(), OrderId = orderId, ParentNodeId = parentId, Code = code, Name = code, NodeType = nodeType, RequiredQty = requiredQty, CompletedQty = completedQty, Status = completedQty >= requiredQty ? "done" : "in_progress", RiskLevel = "normal", }; } }