182 lines
6.6 KiB
PHP
182 lines
6.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Service\WorkHours;
|
|
|
|
use App\Entity\Contract;
|
|
use App\Service\Contracts\EmployeeContractResolver;
|
|
use App\Service\PublicHolidayServiceInterface;
|
|
use App\Service\WorkHours\DailyReferenceMinutesResolver;
|
|
use App\Service\WorkHours\HolidayVirtualHoursResolver;
|
|
use DateTimeImmutable;
|
|
use PHPUnit\Framework\TestCase;
|
|
use RuntimeException;
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
final class HolidayVirtualHoursResolverTest extends TestCase
|
|
{
|
|
private HolidayVirtualHoursResolver $resolver;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$holidayService = $this->createStub(PublicHolidayServiceInterface::class);
|
|
$holidayService->method('getHolidaysDayByYears')->willReturnCallback(
|
|
static fn (string $zone, string $year): array => [
|
|
// Mon 14/07/2025 (lundi)
|
|
'2025-07-14' => '14 juillet',
|
|
// Fri 15/08/2025 (vendredi)
|
|
'2025-08-15' => '15 août',
|
|
// Sat 11/11/2025 (samedi)
|
|
'2025-11-15' => 'Samedi test',
|
|
// Thu 25/12/2025
|
|
'2025-12-25' => 'Noël',
|
|
]
|
|
);
|
|
|
|
$this->resolver = new HolidayVirtualHoursResolver(
|
|
new DailyReferenceMinutesResolver(),
|
|
$holidayService,
|
|
$this->createStub(EmployeeContractResolver::class),
|
|
);
|
|
}
|
|
|
|
public function testReturnsZeroWhenContractIsNull(): void
|
|
{
|
|
self::assertSame(0, $this->resolver->resolveVirtualCredit(null, new DateTimeImmutable('2025-07-14')));
|
|
}
|
|
|
|
public function testReturnsZeroForForfaitPresenceContract(): void
|
|
{
|
|
$contract = new Contract()
|
|
->setName('Forfait')
|
|
->setTrackingMode('PRESENCE')
|
|
->setWeeklyHours(null)
|
|
;
|
|
|
|
self::assertSame(0, $this->resolver->resolveVirtualCredit($contract, new DateTimeImmutable('2025-07-14')));
|
|
}
|
|
|
|
public function testReturnsZeroWhenDayIsNotHoliday(): void
|
|
{
|
|
$contract = $this->build35hContract();
|
|
self::assertSame(0, $this->resolver->resolveVirtualCredit($contract, new DateTimeImmutable('2025-07-07')));
|
|
}
|
|
|
|
public function testReturnsZeroWhenHolidayFallsOnSaturday(): void
|
|
{
|
|
$contract = $this->build35hContract();
|
|
self::assertSame(0, $this->resolver->resolveVirtualCredit($contract, new DateTimeImmutable('2025-11-15')));
|
|
}
|
|
|
|
public function test35hMondayGetsSevenHours(): void
|
|
{
|
|
$contract = $this->build35hContract();
|
|
self::assertSame(7 * 60, $this->resolver->resolveVirtualCredit($contract, new DateTimeImmutable('2025-07-14')));
|
|
}
|
|
|
|
public function test39hMondayGetsEightHours(): void
|
|
{
|
|
$contract = $this->build39hContract();
|
|
self::assertSame(8 * 60, $this->resolver->resolveVirtualCredit($contract, new DateTimeImmutable('2025-07-14')));
|
|
}
|
|
|
|
public function test39hFridayGetsSevenHours(): void
|
|
{
|
|
$contract = $this->build39hContract();
|
|
self::assertSame(7 * 60, $this->resolver->resolveVirtualCredit($contract, new DateTimeImmutable('2025-08-15')));
|
|
}
|
|
|
|
public function testCustomContractUsesProRataReference(): void
|
|
{
|
|
$contract = new Contract()
|
|
->setName('28h')
|
|
->setTrackingMode('TIME')
|
|
->setWeeklyHours(28)
|
|
;
|
|
// 28h / 5 = 5.6h = 336 min
|
|
self::assertSame(336, $this->resolver->resolveVirtualCredit($contract, new DateTimeImmutable('2025-07-14')));
|
|
}
|
|
|
|
public function testInterimContractAlsoReceivesCredit(): void
|
|
{
|
|
$contract = new Contract()
|
|
->setName('Interim')
|
|
->setTrackingMode('TIME')
|
|
->setWeeklyHours(35)
|
|
;
|
|
|
|
self::assertSame(7 * 60, $this->resolver->resolveVirtualCredit($contract, new DateTimeImmutable('2025-07-14')));
|
|
}
|
|
|
|
public function testEffectiveDailyMinutesReturnsActualWhenGreaterThanReference(): void
|
|
{
|
|
$contract = $this->build39hContract();
|
|
// 10h worked on a férié Monday with 39h contract (ref = 8h)
|
|
self::assertSame(600, $this->resolver->resolveEffectiveDailyMinutes($contract, new DateTimeImmutable('2025-07-14'), 600));
|
|
}
|
|
|
|
public function testEffectiveDailyMinutesReturnsReferenceWhenActualLower(): void
|
|
{
|
|
$contract = $this->build39hContract();
|
|
// 4h worked on a férié Monday with 39h contract (ref = 8h) → 8h
|
|
self::assertSame(8 * 60, $this->resolver->resolveEffectiveDailyMinutes($contract, new DateTimeImmutable('2025-07-14'), 240));
|
|
}
|
|
|
|
public function testEffectiveDailyMinutesDelegatesWhenRuleDoesNotApply(): void
|
|
{
|
|
$contract = $this->build39hContract();
|
|
// Non-holiday day: rule does not apply, return actual
|
|
self::assertSame(420, $this->resolver->resolveEffectiveDailyMinutes($contract, new DateTimeImmutable('2025-07-07'), 420));
|
|
}
|
|
|
|
public function testFallsBackGracefullyWhenHolidayServiceFails(): void
|
|
{
|
|
$failingService = $this->createStub(PublicHolidayServiceInterface::class);
|
|
$failingService->method('getHolidaysDayByYears')->willThrowException(new RuntimeException('boom'));
|
|
|
|
$resolver = new HolidayVirtualHoursResolver(
|
|
new DailyReferenceMinutesResolver(),
|
|
$failingService,
|
|
$this->createStub(EmployeeContractResolver::class),
|
|
);
|
|
|
|
self::assertSame(0, $resolver->resolveVirtualCredit($this->build35hContract(), new DateTimeImmutable('2025-07-14')));
|
|
}
|
|
|
|
public function testScheduledWorkdayGetsCreditOnHoliday(): void
|
|
{
|
|
// 4h contract, schedule Mon 2h + Thu 2h
|
|
$contract = new Contract()->setName('4h')->setTrackingMode('TIME')->setWeeklyHours(4);
|
|
// Holiday 2025-07-14 is a Monday → 120 min credit
|
|
self::assertSame(120, $this->resolver->resolveVirtualCredit($contract, new DateTimeImmutable('2025-07-14'), false, [1 => 120, 4 => 120]));
|
|
}
|
|
|
|
public function testUnscheduledWorkdayGetsZeroOnHoliday(): void
|
|
{
|
|
$contract = new Contract()->setName('4h')->setTrackingMode('TIME')->setWeeklyHours(4);
|
|
// Holiday 2025-07-14 is a Monday but schedule only Tue+Fri → 0
|
|
self::assertSame(0, $this->resolver->resolveVirtualCredit($contract, new DateTimeImmutable('2025-07-14'), false, [2 => 120, 5 => 120]));
|
|
}
|
|
|
|
private function build35hContract(): Contract
|
|
{
|
|
return new Contract()
|
|
->setName('35h')
|
|
->setTrackingMode('TIME')
|
|
->setWeeklyHours(35)
|
|
;
|
|
}
|
|
|
|
private function build39hContract(): Contract
|
|
{
|
|
return new Contract()
|
|
->setName('39h')
|
|
->setTrackingMode('TIME')
|
|
->setWeeklyHours(39)
|
|
;
|
|
}
|
|
}
|