feat: send mailings over MailJet smtp in dedicated worker process

This commit is contained in:
Björn Fromme
2026-08-12 15:47:04 +02:00
parent a5c98025d0
commit e0a9562fc6
14 changed files with 480 additions and 44 deletions
@@ -7,9 +7,11 @@ namespace App\Tests\Service\Teamer;
use App\Email\Mailer;
use App\Entity\Teamer;
use App\Entity\User;
use App\Message\SendTeamerMailing;
use App\Model\TeamerFilterDto;
use App\Model\TeamerMailingDto;
use App\Model\TeamerMailingRecipient;
use App\Model\TeamerMailingRecipientsDto;
use App\Repository\TeamerRepository;
use App\Service\Teamer\TeamerMailingService;
use PHPUnit\Framework\MockObject\MockObject;
@@ -32,6 +34,7 @@ class TeamerMailingServiceTest extends TestCase
$this->teamerRepository,
$this->mailer,
$this->createMock(LoggerInterface::class),
'mailing',
);
}
@@ -101,16 +104,8 @@ class TeamerMailingServiceTest extends TestCase
$this->assertSame(5, $recipients->getTotal());
}
public function testSendPersonalisesSubjectAndMessagePerRecipient(): void
public function testSendMailingPersonalisesSubjectAndMessagePerRecipient(): 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))
@@ -120,19 +115,87 @@ class TeamerMailingServiceTest extends TestCase
})
;
$this->service->sendMailing($this->createMailing(
'Hallo {{vorname}}',
'Servus {{name}}',
$this->createRecipient('Anna', 'Berg', '[email protected]'),
$this->createRecipient('Bea', 'Ohm', '[email protected]')
));
$this->assertSame([
['[email protected]', 'Hallo Anna', 'Servus Anna Berg'],
['[email protected]', 'Hallo Bea', 'Servus Bea Ohm'],
], $sent);
}
/**
* Without both transports a mailing silently leaves over the webhoster's relay on the
* shared queue - it still arrives, so nothing about it looks broken, which is exactly
* why it is pinned here.
*/
public function testSendMailingGoesOutOverTheMailingTransports(): void
{
$this->mailer
->expects($this->once())
->method('createAndSendEmail')
->willReturnCallback(function (array $context, array $options): void {
$this->assertSame('mailing', $options['transport']);
$this->assertSame('mailing', $options['bus_transport']);
})
;
$this->service->sendMailing($this->createMailing(
'Betreff',
'Nachricht',
$this->createRecipient('Anna', 'Berg', '[email protected]')
));
}
public function testSendMailingWithoutRecipientsSendsNothing(): void
{
$this->mailer
->expects($this->never())
->method('createAndSendEmail')
;
$this->service->sendMailing($this->createMailing('Betreff', 'Nachricht'));
}
/**
* A preview has to stay on the default relay and the default queue, or composing a
* mailing would wait on the same worker the mailing itself is queued behind.
*/
public function testPreviewLeavesTheTransportsAlone(): void
{
$this->mailer
->expects($this->once())
->method('createAndSendEmail')
->willReturnCallback(function (array $context, array $options): void {
$this->assertNull($options['transport']);
$this->assertNull($options['bus_transport']);
})
;
$mailingDto = (new TeamerMailingDto())
->setSubject('Hallo {{vorname}}')
->setMessage('Servus {{name}}')
;
$recipients = $this->service->resolveRecipients(new TeamerFilterDto());
$count = $this->service->send($mailingDto, $recipients);
$this->service->sendPreview($mailingDto, (new User())->setEmail('[email protected]'));
}
$this->assertSame(2, $count);
$this->assertSame([
['[email protected]', 'Hallo Anna', 'Servus Anna Berg'],
['[email protected]', 'Hallo Bea', 'Servus Bea Ohm'],
], $sent);
public function testMailingSnapshotSkipsRecipientsWithoutAnAddress(): void
{
$recipients = (new TeamerMailingRecipientsDto())
->addEligible($this->createRecipient('Anna', 'Berg', '[email protected]'))
->addEligible($this->createRecipient('Dana', 'Elf', null))
;
$mailing = SendTeamerMailing::fromRecipients('Betreff', 'Nachricht', $recipients);
$this->assertSame(1, $mailing->getRecipientCount());
$this->assertSame('[email protected]', $mailing->getRecipients()[0]['email']);
$this->assertSame('Anna Berg', $mailing->getRecipients()[0]['fullName']);
}
public function testSendPreviewUsesAdminOwnNameAndMarksTheSubject(): void
@@ -191,6 +254,17 @@ class TeamerMailingServiceTest extends TestCase
$this->assertSame('rita.kern', $values[TeamerMailingService::PLACEHOLDER_FULL_NAME]);
}
private function createMailing(string $subject, string $message, TeamerMailingRecipient ...$recipients): SendTeamerMailing
{
$dto = new TeamerMailingRecipientsDto();
foreach ($recipients as $recipient) {
$dto->addEligible($recipient);
}
return SendTeamerMailing::fromRecipients($subject, $message, $dto);
}
private function createRecipient(
string $firstName,
string $lastName,