Files
myep-team/tests/Service/Cron/WorkMonthReminderServiceTest.php
T
2026-10-01 10:07:05 +02:00

130 lines
4.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Service\Cron;
use App\Config\EmailTextKey;
use App\Email\Mailer;
use App\Entity\Embeddable\Communication;
use App\Entity\Teamer;
use App\Entity\TeamerWorkMonth;
use App\Repository\TeamerWorkMonthRepository;
use App\Service\Cron\WorkMonthReminderService;
use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;
/**
* Pins the rules the reminder applies: which day it runs on, how long it waits after an
* import, and who it can actually reach. The query itself is the repository's business.
*/
#[AllowMockObjectsWithoutExpectations]
class WorkMonthReminderServiceTest extends TestCase
{
private TeamerWorkMonthRepository&MockObject $repository;
private Mailer&MockObject $mailer;
private WorkMonthReminderService $service;
protected function setUp(): void
{
$this->repository = $this->createMock(TeamerWorkMonthRepository::class);
$this->mailer = $this->createMock(Mailer::class);
$this->service = new WorkMonthReminderService(
$this->repository,
$this->mailer,
new NullLogger(),
);
}
/**
* app:cron runs daily, so the weekday gate is the only thing stopping a teamer being
* chased every morning.
*/
public function testItOnlyRunsOnMondays(): void
{
$this->repository->expects($this->never())->method('findAwaitingDocument');
$this->mailer->expects($this->never())->method('createAndSendText');
$this->assertSame(
'Work month reminders are only sent on mondays',
$this->service->sendReminders(new \DateTimeImmutable('2026-03-10')),
);
}
public function testItLeavesTheTeamerAGracePeriodAfterTheImport(): void
{
$this->repository
->expects($this->once())
->method('findAwaitingDocument')
->with($this->callback(
static fn (\DateTimeImmutable $cutoff) => '2026-03-04' === $cutoff->format('Y-m-d')
))
->willReturn([])
;
// 9 March 2026 is a Monday; the import ran on the 10th of the month before.
$this->service->sendReminders(new \DateTimeImmutable('2026-03-09'));
}
public function testItMailsOneReminderPerOpenMonth(): void
{
$this->repository->method('findAwaitingDocument')->willReturn([
$this->createWorkMonth(2026, 1),
$this->createWorkMonth(2026, 2),
]);
$sent = [];
$this->mailer
->expects($this->exactly(2))
->method('createAndSendText')
->willReturnCallback(function (EmailTextKey $key, array $placeholders, array $options) use (&$sent) {
$sent[] = [$key, $placeholders, $options];
})
;
$this->assertSame(
'Sent 2 work month reminders to teamers',
$this->service->sendReminders(new \DateTimeImmutable('2026-03-09')),
);
$this->assertSame(EmailTextKey::REMINDER_WORK_MONTH_UPLOAD, $sent[0][0]);
$this->assertSame('Januar 2026', $sent[0][1]['workMonth']);
$this->assertSame('Februar 2026', $sent[1][1]['workMonth']);
$this->assertSame('[email protected]', $sent[0][2]['to']);
}
public function testATeamerWithoutAnAddressIsSkippedSilently(): void
{
$this->repository->method('findAwaitingDocument')->willReturn([
$this->createWorkMonth(2026, 1, null),
$this->createWorkMonth(2026, 2, ' '),
$this->createWorkMonth(2026, 3),
]);
$this->mailer->expects($this->once())->method('createAndSendText');
$this->assertSame(
'Sent 1 work month reminders to teamers',
$this->service->sendReminders(new \DateTimeImmutable('2026-03-09')),
);
}
private function createWorkMonth(int $year, int $month, ?string $email = '[email protected]'): TeamerWorkMonth
{
$communication = new Communication();
$communication->setEmail($email);
$teamer = new Teamer();
$teamer->setFirstName('Theo')->setLastName('Test')->setCommunication($communication);
return (new TeamerWorkMonth())
->setTeamer($teamer)
->setYear($year)
->setMonth($month)
;
}
}