Files
Gmao-app-win11/tests/App.Core.Tests/Kpi/KpiCalculatorTests.cs
2026-09-10 16:43:09 +02:00

147 lines
4.7 KiB
C#

using App.Core.Entities.Maintenance;
using App.Core.Services.Kpi;
namespace App.Core.Tests.Kpi;
public class KpiCalculatorTests
{
[Fact]
public void ComputeMtbfHours_LessThanTwoFailures_ReturnsNull()
{
var timestamps = new List<DateTime> { new(2026, 1, 1) };
Assert.Null(KpiCalculator.ComputeMtbfHours(timestamps));
Assert.Null(KpiCalculator.ComputeMtbfHours(new List<DateTime>()));
}
[Fact]
public void ComputeMtbfHours_AveragesIntervalsBetweenFailures()
{
// Pannes le 1er, le 3 (48h après) et le 8 (120h après) -> moyenne (48+120)/2 = 84h.
var timestamps = new List<DateTime>
{
new(2026, 1, 1),
new(2026, 1, 3),
new(2026, 1, 8),
};
var mtbf = KpiCalculator.ComputeMtbfHours(timestamps);
Assert.Equal(84, mtbf);
}
[Fact]
public void ComputeMttrHours_NoCompletedCorrective_ReturnsNull()
{
var workOrders = new List<WorkOrder>
{
new() { Type = WorkOrderType.Corrective, Status = WorkOrderStatus.InProgress, CreatedAtUtc = DateTime.UtcNow, MachineId = 1, ScheduledDate = DateOnly.FromDateTime(DateTime.UtcNow) },
};
Assert.Null(KpiCalculator.ComputeMttrHours(workOrders));
}
[Fact]
public void ComputeMttrHours_AveragesRepairDuration()
{
var created = new DateTime(2026, 1, 1, 8, 0, 0, DateTimeKind.Utc);
var workOrders = new List<WorkOrder>
{
new()
{
Type = WorkOrderType.Corrective,
Status = WorkOrderStatus.Done,
MachineId = 1,
ScheduledDate = DateOnly.FromDateTime(created),
CreatedAtUtc = created,
CompletedAtUtc = created.AddHours(4),
},
new()
{
Type = WorkOrderType.Corrective,
Status = WorkOrderStatus.Done,
MachineId = 1,
ScheduledDate = DateOnly.FromDateTime(created),
CreatedAtUtc = created,
CompletedAtUtc = created.AddHours(8),
},
};
Assert.Equal(6, KpiCalculator.ComputeMttrHours(workOrders));
}
[Fact]
public void ComputeAvailabilityPercent_NullInputs_ReturnsNull()
{
Assert.Null(KpiCalculator.ComputeAvailabilityPercent(null, 5));
Assert.Null(KpiCalculator.ComputeAvailabilityPercent(5, null));
}
[Fact]
public void ComputeAvailabilityPercent_ComputesClassicFormula()
{
// MTBF=90, MTTR=10 -> 90/(90+10) = 90%
var availability = KpiCalculator.ComputeAvailabilityPercent(90, 10);
Assert.Equal(90.0, availability);
}
[Fact]
public void ComputeMaintenanceCost_SumsPartsAndLabor()
{
var parts = new List<(decimal UnitPrice, double Quantity)>
{
(10.00m, 3), // 30
(5.50m, 2), // 11
};
var cost = KpiCalculator.ComputeMaintenanceCost(parts, laborCost: 100m);
Assert.Equal(141.00m, cost);
}
[Fact]
public void ComputePreventiveComplianceRatePercent_NoClosedOrders_ReturnsNull()
{
var workOrders = new List<WorkOrder>
{
new() { Type = WorkOrderType.Preventive, Status = WorkOrderStatus.ToDo, MachineId = 1, ScheduledDate = new DateOnly(2026, 1, 1), CreatedAtUtc = DateTime.UtcNow },
};
Assert.Null(KpiCalculator.ComputePreventiveComplianceRatePercent(workOrders));
}
[Fact]
public void ComputePreventiveComplianceRatePercent_CountsOnTimeVsLate()
{
var workOrders = new List<WorkOrder>
{
// Clôturé avant échéance -> dans les temps
new()
{
Type = WorkOrderType.Preventive, Status = WorkOrderStatus.Done, MachineId = 1,
ScheduledDate = new DateOnly(2026, 1, 10), CreatedAtUtc = DateTime.UtcNow,
CompletedAtUtc = new DateTime(2026, 1, 9),
},
// Clôturé après échéance -> en retard
new()
{
Type = WorkOrderType.Preventive, Status = WorkOrderStatus.Done, MachineId = 1,
ScheduledDate = new DateOnly(2026, 1, 10), CreatedAtUtc = DateTime.UtcNow,
CompletedAtUtc = new DateTime(2026, 1, 15),
},
// Curatif : ignoré du calcul
new()
{
Type = WorkOrderType.Corrective, Status = WorkOrderStatus.Done, MachineId = 1,
ScheduledDate = new DateOnly(2026, 1, 10), CreatedAtUtc = DateTime.UtcNow,
CompletedAtUtc = new DateTime(2026, 1, 20),
},
};
var rate = KpiCalculator.ComputePreventiveComplianceRatePercent(workOrders);
Assert.Equal(50.0, rate);
}
}