feat: mailing to filtered teamer list
addresses #869dv9bur
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Service\Teamer;
|
||||
|
||||
use App\Email\Mailer;
|
||||
use App\Entity\Teamer;
|
||||
use App\Entity\User;
|
||||
use App\Model\TeamerFilterDto;
|
||||
use App\Model\TeamerMailingDto;
|
||||
use App\Model\TeamerMailingRecipient;
|
||||
use App\Repository\TeamerRepository;
|
||||
use App\Service\Teamer\TeamerMailingService;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class TeamerMailingServiceTest extends TestCase
|
||||
{
|
||||
private TeamerRepository&MockObject $teamerRepository;
|
||||
private Mailer&MockObject $mailer;
|
||||
private TeamerMailingService $service;
|
||||
private static int $nextId = 1;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->teamerRepository = $this->createMock(TeamerRepository::class);
|
||||
$this->mailer = $this->createMock(Mailer::class);
|
||||
|
||||
$this->service = new TeamerMailingService(
|
||||
$this->teamerRepository,
|
||||
$this->mailer,
|
||||
$this->createMock(LoggerInterface::class),
|
||||
);
|
||||
}
|
||||
|
||||
public function testRenderReplacesAllNamePlaceholders(): void
|
||||
{
|
||||
$recipient = $this->createRecipient('Anna', 'Berg', '[email protected]');
|
||||
|
||||
$rendered = $this->service->render(
|
||||
'Hallo {{vorname}} {{nachname}}, alias {{name}}!',
|
||||
$this->service->placeholderValuesFor($recipient)
|
||||
);
|
||||
|
||||
$this->assertSame('Hallo Anna Berg, alias Anna Berg!', $rendered);
|
||||
}
|
||||
|
||||
public function testRenderLeavesUnknownPlaceholdersUntouched(): void
|
||||
{
|
||||
$recipient = $this->createRecipient('Anna', 'Berg', '[email protected]');
|
||||
|
||||
$rendered = $this->service->render(
|
||||
'Hallo {{vorname}}, {{unbekannt}} bleibt stehen',
|
||||
$this->service->placeholderValuesFor($recipient)
|
||||
);
|
||||
|
||||
$this->assertSame('Hallo Anna, {{unbekannt}} bleibt stehen', $rendered);
|
||||
}
|
||||
|
||||
public function testRecipientIsBuiltFromARepositoryRow(): void
|
||||
{
|
||||
$deletedAt = new \DateTimeImmutable('2026-01-02 03:04:05');
|
||||
|
||||
$recipient = TeamerMailingRecipient::fromRow([
|
||||
'id' => 42,
|
||||
'firstName' => 'Anna',
|
||||
'lastName' => 'Berg',
|
||||
'email' => '[email protected]',
|
||||
'deletedAt' => $deletedAt,
|
||||
'disabledAt' => null,
|
||||
]);
|
||||
|
||||
$this->assertSame(42, $recipient->getId());
|
||||
$this->assertSame('Anna Berg', $recipient->getFullName());
|
||||
$this->assertSame('[email protected]', $recipient->getEmail());
|
||||
$this->assertTrue($recipient->isDeleted());
|
||||
$this->assertFalse($recipient->isDisabled());
|
||||
}
|
||||
|
||||
public function testResolveRecipientsPartitionsBySkipReason(): void
|
||||
{
|
||||
$eligible = $this->createRecipient('Anna', 'Berg', '[email protected]');
|
||||
$deleted = $this->createRecipient('Bea', 'Ohm', '[email protected]', deleted: true);
|
||||
$disabled = $this->createRecipient('Cem', 'Dal', '[email protected]', disabled: true);
|
||||
$noEmail = $this->createRecipient('Dana', 'Elf', null);
|
||||
$blankEmail = $this->createRecipient('Emil', 'Fux', ' ');
|
||||
|
||||
$this->teamerRepository
|
||||
->method('getMailingRecipients')
|
||||
->willReturn([$eligible, $deleted, $disabled, $noEmail, $blankEmail])
|
||||
;
|
||||
|
||||
$recipients = $this->service->resolveRecipients(new TeamerFilterDto());
|
||||
|
||||
$this->assertSame([$eligible], $recipients->getEligible());
|
||||
$this->assertSame(1, $recipients->getSkippedDeleted());
|
||||
$this->assertSame(1, $recipients->getSkippedDisabled());
|
||||
$this->assertSame(2, $recipients->getSkippedNoEmail());
|
||||
$this->assertSame(5, $recipients->getTotal());
|
||||
}
|
||||
|
||||
public function testSendPersonalisesSubjectAndMessagePerRecipient(): void
|
||||
{
|
||||
$this->teamerRepository
|
||||
->method('getMailingRecipients')
|
||||
->willReturn([
|
||||
$this->createRecipient('Anna', 'Berg', '[email protected]'),
|
||||
$this->createRecipient('Bea', 'Ohm', '[email protected]'),
|
||||
])
|
||||
;
|
||||
|
||||
$sent = [];
|
||||
$this->mailer
|
||||
->expects($this->exactly(2))
|
||||
->method('createAndSendEmail')
|
||||
->willReturnCallback(function (array $context, array $options) use (&$sent): void {
|
||||
$sent[] = [$options['to'], $options['subject'], $context['message']];
|
||||
})
|
||||
;
|
||||
|
||||
$mailingDto = (new TeamerMailingDto())
|
||||
->setSubject('Hallo {{vorname}}')
|
||||
->setMessage('Servus {{name}}')
|
||||
;
|
||||
|
||||
$recipients = $this->service->resolveRecipients(new TeamerFilterDto());
|
||||
$count = $this->service->send($mailingDto, $recipients);
|
||||
|
||||
$this->assertSame(2, $count);
|
||||
$this->assertSame([
|
||||
['[email protected]', 'Hallo Anna', 'Servus Anna Berg'],
|
||||
['[email protected]', 'Hallo Bea', 'Servus Bea Ohm'],
|
||||
], $sent);
|
||||
}
|
||||
|
||||
public function testSendPreviewUsesAdminOwnNameAndMarksTheSubject(): void
|
||||
{
|
||||
$admin = (new User())
|
||||
->setEmail('[email protected]')
|
||||
->setFirstName('Rita')
|
||||
->setLastName('Kern')
|
||||
;
|
||||
|
||||
$this->mailer
|
||||
->expects($this->once())
|
||||
->method('createAndSendEmail')
|
||||
->with(
|
||||
['message' => 'Servus Rita Kern'],
|
||||
$this->callback(function (array $options): bool {
|
||||
$this->assertSame('[email protected]', $options['to']);
|
||||
$this->assertSame('[Vorschau] Hallo Rita', $options['subject']);
|
||||
|
||||
return true;
|
||||
})
|
||||
)
|
||||
;
|
||||
|
||||
$mailingDto = (new TeamerMailingDto())
|
||||
->setSubject('Hallo {{vorname}}')
|
||||
->setMessage('Servus {{name}}')
|
||||
;
|
||||
|
||||
$this->service->sendPreview($mailingDto, $admin);
|
||||
}
|
||||
|
||||
public function testSendPreviewPrefersTheAdminsOwnTeamerRecord(): void
|
||||
{
|
||||
$teamer = (new Teamer())->setFirstName('Rita')->setLastName('Kern-Teamer');
|
||||
$admin = (new User())
|
||||
->setEmail('[email protected]')
|
||||
->setFirstName('Rita')
|
||||
->setLastName('Kern-User')
|
||||
->setTeamer($teamer)
|
||||
;
|
||||
|
||||
$this->assertSame(
|
||||
'Kern-Teamer',
|
||||
$this->service->placeholderValuesForUser($admin)[TeamerMailingService::PLACEHOLDER_LAST_NAME]
|
||||
);
|
||||
}
|
||||
|
||||
public function testPreviewFallsBackToTheAddressWhenTheAdminHasNoName(): void
|
||||
{
|
||||
$admin = (new User())->setEmail('[email protected]');
|
||||
|
||||
$values = $this->service->placeholderValuesForUser($admin);
|
||||
|
||||
$this->assertSame('rita.kern', $values[TeamerMailingService::PLACEHOLDER_FIRST_NAME]);
|
||||
$this->assertSame('rita.kern', $values[TeamerMailingService::PLACEHOLDER_FULL_NAME]);
|
||||
}
|
||||
|
||||
private function createRecipient(
|
||||
string $firstName,
|
||||
string $lastName,
|
||||
?string $email,
|
||||
bool $deleted = false,
|
||||
bool $disabled = false,
|
||||
): TeamerMailingRecipient {
|
||||
$now = new \DateTimeImmutable();
|
||||
|
||||
return new TeamerMailingRecipient(
|
||||
self::$nextId++,
|
||||
$firstName,
|
||||
$lastName,
|
||||
$email,
|
||||
$deleted ? $now : null,
|
||||
$disabled ? $now : null
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user