129 lines
4.5 KiB
PHP
129 lines
4.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Validator;
|
|
|
|
use App\Config\SeasonCalendar;
|
|
use App\Entity\SeasonAssignment;
|
|
use App\Entity\Teamer;
|
|
use App\Repository\SeasonAssignmentRepository;
|
|
use App\Validator\Constraints\SeasonAssignment as SeasonAssignmentConstraint;
|
|
use App\Validator\Constraints\SeasonAssignmentValidator;
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
|
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
|
|
|
|
/**
|
|
* "One season assignment per teamer per season" was a stated fact before this rule; what is
|
|
* pinned here is which season window a given assignment is counted against, and the cases that
|
|
* must not be refused - an unstaffed assignment, and the assignment being edited itself.
|
|
*/
|
|
class SeasonAssignmentValidatorTest extends ConstraintValidatorTestCase
|
|
{
|
|
private SeasonAssignmentRepository&MockObject $repository;
|
|
|
|
protected function createValidator(): SeasonAssignmentValidator
|
|
{
|
|
$this->repository = $this->createMock(SeasonAssignmentRepository::class);
|
|
|
|
return new SeasonAssignmentValidator($this->repository, new SeasonCalendar('11-01', '04-01'));
|
|
}
|
|
|
|
public function testASecondAssignmentInTheSameSeasonIsRefused(): void
|
|
{
|
|
$this->repository->method('countInSeasonWindow')->willReturn(1);
|
|
|
|
$this->validator->validate($this->seasonAssignment('2026-12-20'), $this->constraint());
|
|
|
|
$this->buildViolation('Für diese:n Teamer:in existiert in dieser Saison bereits ein Saisoneinsatz')
|
|
->assertRaised()
|
|
;
|
|
}
|
|
|
|
public function testTheFirstAssignmentOfASeasonPasses(): void
|
|
{
|
|
$this->repository->method('countInSeasonWindow')->willReturn(0);
|
|
|
|
$this->validator->validate($this->seasonAssignment('2026-12-20'), $this->constraint());
|
|
|
|
$this->assertNoViolation();
|
|
}
|
|
|
|
public function testItCountsAgainstTheSeasonTheAssignmentStartsIn(): void
|
|
{
|
|
// 20.02.2027 belongs to the season that began on 01.11.2026, so the window asked for has
|
|
// to be that one and not the calendar year the date happens to fall in
|
|
$this->repository
|
|
->expects($this->once())
|
|
->method('countInSeasonWindow')
|
|
->with(
|
|
$this->isInstanceOf(Teamer::class),
|
|
$this->callback(static fn (\DateTimeImmutable $start): bool => '2026-11-01' === $start->format('Y-m-d')),
|
|
$this->callback(static fn (\DateTimeImmutable $end): bool => '2027-04-01' === $end->format('Y-m-d')),
|
|
null
|
|
)
|
|
->willReturn(0)
|
|
;
|
|
|
|
$this->validator->validate($this->seasonAssignment('2027-02-20'), $this->constraint());
|
|
|
|
$this->assertNoViolation();
|
|
}
|
|
|
|
public function testEditingAnAssignmentDoesNotCountItAgainstItself(): void
|
|
{
|
|
$seasonAssignment = $this->seasonAssignment('2026-12-20');
|
|
|
|
// the id is never set by hand in the app; it is what tells the query to leave the row
|
|
// being edited out, so an edit that changes nothing cannot refuse itself
|
|
$id = new \ReflectionProperty(SeasonAssignment::class, 'id');
|
|
$id->setValue($seasonAssignment, 42);
|
|
|
|
$this->repository
|
|
->expects($this->once())
|
|
->method('countInSeasonWindow')
|
|
->with($this->anything(), $this->anything(), $this->anything(), 42)
|
|
->willReturn(0)
|
|
;
|
|
|
|
$this->validator->validate($seasonAssignment, $this->constraint());
|
|
|
|
$this->assertNoViolation();
|
|
}
|
|
|
|
public function testAnAssignmentWithoutATeamerPasses(): void
|
|
{
|
|
$this->repository->expects($this->never())->method('countInSeasonWindow');
|
|
|
|
$seasonAssignment = $this->seasonAssignment('2026-12-20')->setTeamer(null);
|
|
|
|
$this->validator->validate($seasonAssignment, $this->constraint());
|
|
|
|
$this->assertNoViolation();
|
|
}
|
|
|
|
public function testAnAssignmentOutsideAnySeasonWindowPasses(): void
|
|
{
|
|
$this->repository->expects($this->never())->method('countInSeasonWindow');
|
|
|
|
$this->validator->validate($this->seasonAssignment('2027-07-15'), $this->constraint());
|
|
|
|
$this->assertNoViolation();
|
|
}
|
|
|
|
private function constraint(): SeasonAssignmentConstraint
|
|
{
|
|
return new SeasonAssignmentConstraint();
|
|
}
|
|
|
|
private function seasonAssignment(string $dateFrom): SeasonAssignment
|
|
{
|
|
return (new SeasonAssignment())
|
|
->setTeamer(new Teamer())
|
|
->setDateFrom(new \DateTimeImmutable($dateFrom))
|
|
->setDateTo(new \DateTimeImmutable($dateFrom.' +6 months'))
|
|
->setHotelCode('AGR')
|
|
;
|
|
}
|
|
}
|