97 lines
3.9 KiB
PHP
97 lines
3.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Repository;
|
|
|
|
use App\Entity\SeasonAssignmentTask;
|
|
use App\Repository\SeasonAssignmentTaskRepository;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Doctrine\ORM\QueryBuilder;
|
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
|
|
|
/**
|
|
* The reminders are only as good as the set of rows they start from, so the conditions are
|
|
* compiled and pinned here - the suite has no fixtures.
|
|
*
|
|
* Note what is deliberately absent: the deadlines. They are a relative offset off the
|
|
* assignment's first day, and month arithmetic is not invertible day by day, so they are
|
|
* resolved in PHP rather than reproduced in DQL.
|
|
*/
|
|
class SeasonAssignmentTaskRepositoryTest extends KernelTestCase
|
|
{
|
|
public function testOpenTasksExcludeDoneOnesAndDeletedRecords(): void
|
|
{
|
|
$dql = $this->createOpenQueryBuilder()->getQuery()->getDQL();
|
|
|
|
$this->assertStringContainsString('task.done = :done', $dql);
|
|
$this->assertStringContainsString('season_assignment.deletedAt IS NULL', $dql);
|
|
$this->assertStringContainsString('teamer.deletedAt IS NULL', $dql);
|
|
// the period is the assignment's own, so there is no destination left to join
|
|
$this->assertStringNotContainsString('destination', $dql);
|
|
}
|
|
|
|
/**
|
|
* An inner join, not a left one: an assignment the office has laid out but not staffed yet
|
|
* has nobody to remind and nobody who could be late.
|
|
*/
|
|
public function testAnUnstaffedAssignmentIsLeftOut(): void
|
|
{
|
|
$dql = $this->createOpenQueryBuilder()->getQuery()->getDQL();
|
|
|
|
$this->assertStringContainsString('INNER JOIN season_assignment.teamer teamer', $dql);
|
|
$this->assertStringNotContainsString('LEFT JOIN', $dql);
|
|
}
|
|
|
|
public function testTheSeasonWindowIsOptional(): void
|
|
{
|
|
$unscoped = $this->createOpenQueryBuilder()->getQuery()->getDQL();
|
|
$scoped = $this
|
|
->createOpenQueryBuilder(new \DateTimeImmutable('2026-11-01'), new \DateTimeImmutable('2027-04-01'))
|
|
->getQuery()
|
|
->getDQL()
|
|
;
|
|
|
|
$this->assertStringNotContainsString('windowStart', $unscoped);
|
|
$this->assertStringContainsString('season_assignment.dateFrom >= :windowStart', $scoped);
|
|
$this->assertStringContainsString('season_assignment.dateFrom <= :windowEnd', $scoped);
|
|
}
|
|
|
|
/**
|
|
* What the propagate action may still rewrite: an entry that is not ticked off, on an
|
|
* assignment that has not ended. A finished entry keeps the document it handed out.
|
|
*/
|
|
public function testPropagationSkipsTickedEntriesAndEndedAssignments(): void
|
|
{
|
|
/** @var EntityManagerInterface $entityManager */
|
|
$entityManager = static::getContainer()->get(EntityManagerInterface::class);
|
|
|
|
/** @var SeasonAssignmentTaskRepository $repository */
|
|
$repository = $entityManager->getRepository(SeasonAssignmentTask::class);
|
|
|
|
$dql = $repository
|
|
->createOpenForTaskQueryBuilder('contract', new \DateTimeImmutable('2026-11-01'))
|
|
->getQuery()
|
|
->getDQL()
|
|
;
|
|
|
|
$this->assertStringContainsString('task.taskKey = :taskKey', $dql);
|
|
$this->assertStringContainsString('task.done = :done', $dql);
|
|
$this->assertStringContainsString('season_assignment.dateTo >= :today', $dql);
|
|
$this->assertStringContainsString('season_assignment.deletedAt IS NULL', $dql);
|
|
}
|
|
|
|
private function createOpenQueryBuilder(
|
|
?\DateTimeImmutable $windowStart = null,
|
|
?\DateTimeImmutable $windowEnd = null,
|
|
): QueryBuilder {
|
|
/** @var EntityManagerInterface $entityManager */
|
|
$entityManager = static::getContainer()->get(EntityManagerInterface::class);
|
|
|
|
/** @var SeasonAssignmentTaskRepository $repository */
|
|
$repository = $entityManager->getRepository(SeasonAssignmentTask::class);
|
|
|
|
return $repository->createOpenQueryBuilder($windowStart, $windowEnd);
|
|
}
|
|
}
|