Files
Lesstime/tests/Unit/Service/AbsenceDayCalculatorTest.php
T
Matthieu 45e86543a1 fix(absences) : demi-journée mono-jour décomptée 0,5 (et non 0)
Sur une demande d'un seul jour, le formulaire recopie la demi-journée de
début sur la fin (même date), si bien que les deux bornes portaient une
demi-journée et 0,5 était soustrait deux fois (1 - 0,5 - 0,5 = 0).

Quand start et end tombent le même jour, les deux bornes se confondent :
on ne soustrait désormais 0,5 qu'une seule fois. Comparaison par
getTimestamp() pour rester compatible strict_comparison (=== sur deux
DateTimeImmutable distincts teste l'identité d'instance, pas la valeur).

Couverture complétée : mono-jour plein, week-end, férié, inversion de
dates, demi-journée de fin seule, demi sur samedi en mode ouvrables.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 10:05:42 +02:00

192 lines
6.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Unit\Service;
use App\Enum\HalfDay;
use App\Service\AbsenceDayCalculator;
use App\Service\PublicHolidayProvider;
use DateTimeImmutable;
use PHPUnit\Framework\TestCase;
/**
* @internal
*/
class AbsenceDayCalculatorTest extends TestCase
{
private AbsenceDayCalculator $calculator;
protected function setUp(): void
{
$this->calculator = new AbsenceDayCalculator(new PublicHolidayProvider());
}
public function testFullWeekIsFiveWorkingDays(): void
{
// Mon 2026-06-01 to Fri 2026-06-05, no holidays that week
self::assertSame(5.0, $this->calculator->countWorkingDays(
new DateTimeImmutable('2026-06-01'),
new DateTimeImmutable('2026-06-05'),
));
}
public function testWeekendIsSkipped(): void
{
// Fri 2026-06-05 to Mon 2026-06-08 => Fri + Mon = 2
self::assertSame(2.0, $this->calculator->countWorkingDays(
new DateTimeImmutable('2026-06-05'),
new DateTimeImmutable('2026-06-08'),
));
}
public function testHolidayIsSkipped(): void
{
// Thu 2026-05-07 to Fri 2026-05-08 (Victoire 1945) => Thu only = 1
self::assertSame(1.0, $this->calculator->countWorkingDays(
new DateTimeImmutable('2026-05-07'),
new DateTimeImmutable('2026-05-08'),
));
}
public function testHalfDayStartSubtractsHalf(): void
{
self::assertSame(4.5, $this->calculator->countWorkingDays(
new DateTimeImmutable('2026-06-01'),
new DateTimeImmutable('2026-06-05'),
HalfDay::Afternoon,
));
}
public function testBothHalfDaysSubtractOne(): void
{
self::assertSame(4.0, $this->calculator->countWorkingDays(
new DateTimeImmutable('2026-06-01'),
new DateTimeImmutable('2026-06-05'),
HalfDay::Afternoon,
HalfDay::Morning,
));
}
public function testSingleFullDayCountsOne(): void
{
// Most common request: a single full working day = 1.0 (no half-day).
self::assertSame(1.0, $this->calculator->countWorkingDays(
new DateTimeImmutable('2026-06-01'),
new DateTimeImmutable('2026-06-01'),
));
}
public function testSingleHalfDay(): void
{
self::assertSame(0.5, $this->calculator->countWorkingDays(
new DateTimeImmutable('2026-06-01'),
new DateTimeImmutable('2026-06-01'),
HalfDay::Morning,
));
}
public function testHalfDayEndOnlySubtractsHalf(): void
{
// Mirror of testHalfDayStartSubtractsHalf: end half-day only, on a
// counted multi-day range => 5 - 0.5 = 4.5.
self::assertSame(4.5, $this->calculator->countWorkingDays(
new DateTimeImmutable('2026-06-01'),
new DateTimeImmutable('2026-06-05'),
null,
HalfDay::Morning,
));
}
public function testEndBeforeStartCountsZero(): void
{
// Inverted range guard (end < start) => 0, never negative.
self::assertSame(0.0, $this->calculator->countWorkingDays(
new DateTimeImmutable('2026-06-05'),
new DateTimeImmutable('2026-06-01'),
));
}
public function testSingleDayOnWeekendCountsZero(): void
{
// Saturday, jours ouvrés: not counted, even with a half-day => 0.
self::assertSame(0.0, $this->calculator->countWorkingDays(
new DateTimeImmutable('2026-06-06'),
new DateTimeImmutable('2026-06-06'),
HalfDay::Morning,
HalfDay::Morning,
));
}
public function testSingleDayOnHolidayCountsZero(): void
{
// Fri 2026-05-08 (Victoire 1945): public holiday => 0.
self::assertSame(0.0, $this->calculator->countWorkingDays(
new DateTimeImmutable('2026-05-08'),
new DateTimeImmutable('2026-05-08'),
));
}
public function testHalfDayOnSaturdayCountedInOpenDays(): void
{
// Fri + Sat in "ouvrables" mode = 2; the end half-day sits on Saturday,
// which IS counted here => 2 - 0.5 = 1.5.
self::assertSame(1.5, $this->calculator->countWorkingDays(
new DateTimeImmutable('2026-06-05'),
new DateTimeImmutable('2026-06-06'),
null,
HalfDay::Morning,
false,
));
}
public function testSingleDayWithBothHalfDaysIsStillHalf(): void
{
// Real-world single-day request: the form mirrors the start half-day
// onto the end (same day), so both boundaries carry a half-day. It must
// still count as 0.5, not 0 (the two boundaries collapse onto one day).
self::assertSame(0.5, $this->calculator->countWorkingDays(
new DateTimeImmutable('2026-06-01'),
new DateTimeImmutable('2026-06-01'),
HalfDay::Morning,
HalfDay::Morning,
));
}
public function testHalfDayOnNonCountedStartIsIgnored(): void
{
// Sat 2026-06-06 → Mon 2026-06-08, jours ouvrés : Sat & Sun skipped, Mon = 1.
// startHalfDay sits on Saturday (not counted) => must NOT subtract 0.5.
self::assertSame(1.0, $this->calculator->countWorkingDays(
new DateTimeImmutable('2026-06-06'),
new DateTimeImmutable('2026-06-08'),
HalfDay::Afternoon,
));
}
public function testHalfDayOnNonCountedEndIsIgnored(): void
{
// Fri 2026-06-05 → Sun 2026-06-07, jours ouvrés : Fri = 1, weekend skipped.
// endHalfDay sits on Sunday (not counted) => must NOT subtract 0.5.
self::assertSame(1.0, $this->calculator->countWorkingDays(
new DateTimeImmutable('2026-06-05'),
new DateTimeImmutable('2026-06-07'),
null,
HalfDay::Morning,
));
}
public function testWorkingDaysVsOpenDays(): void
{
// Fri 2026-06-05 to Mon 2026-06-08, "ouvrables" includes Saturday
// => Fri + Sat + Mon = 3 (Sunday always skipped)
self::assertSame(3.0, $this->calculator->countWorkingDays(
new DateTimeImmutable('2026-06-05'),
new DateTimeImmutable('2026-06-08'),
null,
null,
false,
));
}
}