first commit
This commit is contained in:
174
tests/App.Core.Tests/Maintenance/FrequencyCalculatorTests.cs
Normal file
174
tests/App.Core.Tests/Maintenance/FrequencyCalculatorTests.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
using App.Core.Entities.Maintenance;
|
||||
using App.Core.Services.Maintenance;
|
||||
|
||||
namespace App.Core.Tests.Maintenance;
|
||||
|
||||
public class FrequencyCalculatorTests
|
||||
{
|
||||
private static MaintenancePlan CreatePlan(FrequencyType type, DateOnly startDate) => new()
|
||||
{
|
||||
MachineId = 1,
|
||||
Name = "Test",
|
||||
FrequencyType = type,
|
||||
StartDate = startDate,
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void ComputeNextDueDate_Daily_ReturnsAnchorPlusOneDay()
|
||||
{
|
||||
var plan = CreatePlan(FrequencyType.Daily, new DateOnly(2026, 3, 10));
|
||||
|
||||
var next = FrequencyCalculator.ComputeNextDueDate(plan);
|
||||
|
||||
Assert.Equal(new DateOnly(2026, 3, 11), next);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeNextDueDate_UsesLastExecutionDate_WhenPlanAlreadyRanOnce()
|
||||
{
|
||||
var plan = CreatePlan(FrequencyType.Daily, new DateOnly(2026, 1, 1));
|
||||
plan.LastExecutionDateUtc = new DateOnly(2026, 3, 10);
|
||||
|
||||
var next = FrequencyCalculator.ComputeNextDueDate(plan);
|
||||
|
||||
Assert.Equal(new DateOnly(2026, 3, 11), next);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeNextDueDate_Weekly_ReturnsNextConfiguredWeekday()
|
||||
{
|
||||
// 2026-03-10 est un mardi. Gamme hebdomadaire calée sur le lundi (1).
|
||||
var plan = CreatePlan(FrequencyType.Weekly, new DateOnly(2026, 3, 10));
|
||||
plan.DayOfWeek = (int)DayOfWeek.Monday;
|
||||
|
||||
var next = FrequencyCalculator.ComputeNextDueDate(plan);
|
||||
|
||||
Assert.Equal(new DateOnly(2026, 3, 16), next); // premier lundi après le mardi 10/03
|
||||
Assert.Equal(DayOfWeek.Monday, next.DayOfWeek);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeNextDueDate_Weekly_MissingDayOfWeek_Throws()
|
||||
{
|
||||
var plan = CreatePlan(FrequencyType.Weekly, new DateOnly(2026, 3, 10));
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => FrequencyCalculator.ComputeNextDueDate(plan));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeNextDueDate_Monthly_ClampsToLastDayOfShorterMonth()
|
||||
{
|
||||
// Ancre en janvier, jour du mois = 31 : février n'a pas 31 jours -> clampé au 28/29.
|
||||
var plan = CreatePlan(FrequencyType.Monthly, new DateOnly(2026, 1, 15));
|
||||
plan.DayOfMonth = 31;
|
||||
|
||||
var next = FrequencyCalculator.ComputeNextDueDate(plan);
|
||||
|
||||
Assert.Equal(new DateOnly(2026, 2, 28), next); // 2026 n'est pas bissextile
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeNextDueDate_MonthlyRule_FirstMonday()
|
||||
{
|
||||
// Ancre en janvier -> cible février 2024. Le 1er lundi de février 2024 est le 05/02.
|
||||
var plan = CreatePlan(FrequencyType.MonthlyRule, new DateOnly(2024, 1, 10));
|
||||
plan.MonthlyRuleOccurrence = 1;
|
||||
plan.MonthlyRuleWeekday = (int)DayOfWeek.Monday;
|
||||
|
||||
var next = FrequencyCalculator.ComputeNextDueDate(plan);
|
||||
|
||||
Assert.Equal(new DateOnly(2024, 2, 5), next);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeNextDueDate_MonthlyRule_LastFriday()
|
||||
{
|
||||
// Cible février 2024 (bissextile). Le dernier vendredi de février 2024 est le 23/02.
|
||||
var plan = CreatePlan(FrequencyType.MonthlyRule, new DateOnly(2024, 1, 10));
|
||||
plan.MonthlyRuleOccurrence = -1;
|
||||
plan.MonthlyRuleWeekday = (int)DayOfWeek.Friday;
|
||||
|
||||
var next = FrequencyCalculator.ComputeNextDueDate(plan);
|
||||
|
||||
Assert.Equal(new DateOnly(2024, 2, 23), next);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(FrequencyType.Bimonthly, 2)]
|
||||
[InlineData(FrequencyType.Quarterly, 3)]
|
||||
[InlineData(FrequencyType.Semiannual, 6)]
|
||||
[InlineData(FrequencyType.Annual, 12)]
|
||||
public void ComputeNextDueDate_FixedMonthIntervals_AddsExpectedMonths(FrequencyType type, int months)
|
||||
{
|
||||
var anchor = new DateOnly(2026, 3, 10);
|
||||
var plan = CreatePlan(type, anchor);
|
||||
|
||||
var next = FrequencyCalculator.ComputeNextDueDate(plan);
|
||||
|
||||
Assert.Equal(anchor.AddMonths(months), next);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeNextDueDate_CustomDays_AddsConfiguredInterval()
|
||||
{
|
||||
var plan = CreatePlan(FrequencyType.CustomDays, new DateOnly(2026, 3, 10));
|
||||
plan.CustomIntervalDays = 45;
|
||||
|
||||
var next = FrequencyCalculator.ComputeNextDueDate(plan);
|
||||
|
||||
Assert.Equal(new DateOnly(2026, 4, 24), next);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeNextDueDate_CounterBased_ThrowsNotSupported()
|
||||
{
|
||||
var plan = CreatePlan(FrequencyType.CounterBased, new DateOnly(2026, 3, 10));
|
||||
|
||||
Assert.Throws<NotSupportedException>(() => FrequencyCalculator.ComputeNextDueDate(plan));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeNextCounterThreshold_NoPriorExecution_StartsFromZero()
|
||||
{
|
||||
var plan = CreatePlan(FrequencyType.CounterBased, new DateOnly(2026, 3, 10));
|
||||
plan.CounterThreshold = 500;
|
||||
|
||||
var threshold = FrequencyCalculator.ComputeNextCounterThreshold(plan);
|
||||
|
||||
Assert.Equal(500, threshold);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeNextCounterThreshold_AddsToLastExecutionValue()
|
||||
{
|
||||
var plan = CreatePlan(FrequencyType.CounterBased, new DateOnly(2026, 3, 10));
|
||||
plan.CounterThreshold = 500;
|
||||
plan.LastExecutionCounterValue = 1200;
|
||||
|
||||
var threshold = FrequencyCalculator.ComputeNextCounterThreshold(plan);
|
||||
|
||||
Assert.Equal(1700, threshold);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsCounterWithinLeadWindow_TrueOncePercentReached()
|
||||
{
|
||||
var plan = CreatePlan(FrequencyType.CounterBased, new DateOnly(2026, 3, 10));
|
||||
plan.CounterThreshold = 100;
|
||||
plan.LastExecutionCounterValue = 1000;
|
||||
plan.LeadTimePercentForCounter = 90;
|
||||
|
||||
Assert.False(FrequencyCalculator.IsCounterWithinLeadWindow(plan, currentCounterValue: 1085, defaultLeadTimePercent: 80));
|
||||
Assert.True(FrequencyCalculator.IsCounterWithinLeadWindow(plan, currentCounterValue: 1090, defaultLeadTimePercent: 80));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsCounterWithinLeadWindow_UsesDefaultPercent_WhenPlanDoesNotOverride()
|
||||
{
|
||||
var plan = CreatePlan(FrequencyType.CounterBased, new DateOnly(2026, 3, 10));
|
||||
plan.CounterThreshold = 100;
|
||||
|
||||
Assert.True(FrequencyCalculator.IsCounterWithinLeadWindow(plan, currentCounterValue: 81, defaultLeadTimePercent: 80));
|
||||
Assert.False(FrequencyCalculator.IsCounterWithinLeadWindow(plan, currentCounterValue: 79, defaultLeadTimePercent: 80));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user