Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
345
tests/Framework/Mail/MailerTest.php
Normal file
345
tests/Framework/Mail/MailerTest.php
Normal file
@@ -0,0 +1,345 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Domain\Common\ValueObject\Email;
|
||||
use App\Framework\CommandBus\CommandBus;
|
||||
use App\Framework\Mail\Commands\SendEmailBatchCommand;
|
||||
use App\Framework\Mail\Commands\SendEmailCommand;
|
||||
use App\Framework\Mail\EmailList;
|
||||
use App\Framework\Mail\Mailer;
|
||||
use App\Framework\Mail\Message;
|
||||
use App\Framework\Mail\Testing\MockTransport;
|
||||
use App\Framework\Mail\TransportResult;
|
||||
use App\Framework\Queue\Queue;
|
||||
|
||||
describe('Mailer', function () {
|
||||
beforeEach(function () {
|
||||
$this->transport = new MockTransport();
|
||||
$this->queue = new MailTestQueue();
|
||||
$this->commandBus = new TestCommandBus();
|
||||
$this->mailer = new Mailer($this->transport, $this->queue, $this->commandBus);
|
||||
|
||||
$this->message = new Message(
|
||||
from: new Email('sender@example.com'),
|
||||
subject: 'Test Subject',
|
||||
body: 'Test body',
|
||||
to: new EmailList(new Email('recipient@example.com'))
|
||||
);
|
||||
});
|
||||
|
||||
describe('send method', function () {
|
||||
it('sends email successfully using transport', function () {
|
||||
$result = $this->mailer->send($this->message);
|
||||
|
||||
expect($result)->toBeTrue();
|
||||
expect($this->transport->getSentMessageCount())->toBe(1);
|
||||
expect($this->transport->getLastSentMessage()['message'])->toBe($this->message);
|
||||
});
|
||||
|
||||
it('returns false when transport fails', function () {
|
||||
$this->transport->setShouldFail(true, 'Transport error');
|
||||
|
||||
$result = $this->mailer->send($this->message);
|
||||
|
||||
expect($result)->toBeFalse();
|
||||
expect($this->transport->getSentMessageCount())->toBe(0);
|
||||
});
|
||||
|
||||
it('does not use queue for synchronous sending', function () {
|
||||
$this->mailer->send($this->message);
|
||||
|
||||
expect($this->queue->wasUsed())->toBeFalse();
|
||||
expect($this->commandBus->wasUsed())->toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
describe('queue method', function () {
|
||||
it('dispatches SendEmailCommand with default parameters', function () {
|
||||
$result = $this->mailer->queue($this->message);
|
||||
|
||||
expect($result)->toBeTrue();
|
||||
expect($this->commandBus->wasUsed())->toBeTrue();
|
||||
|
||||
$command = $this->commandBus->getLastCommand();
|
||||
expect($command)->toBeInstanceOf(SendEmailCommand::class);
|
||||
expect($command->message)->toBe($this->message);
|
||||
expect($command->maxRetries)->toBe(3);
|
||||
expect($command->delaySeconds)->toBe(0);
|
||||
});
|
||||
|
||||
it('dispatches SendEmailCommand with custom parameters', function () {
|
||||
$result = $this->mailer->queue($this->message, 5, 30);
|
||||
|
||||
expect($result)->toBeTrue();
|
||||
expect($this->commandBus->wasUsed())->toBeTrue();
|
||||
|
||||
$command = $this->commandBus->getLastCommand();
|
||||
expect($command)->toBeInstanceOf(SendEmailCommand::class);
|
||||
expect($command->message)->toBe($this->message);
|
||||
expect($command->maxRetries)->toBe(5);
|
||||
expect($command->delaySeconds)->toBe(30);
|
||||
});
|
||||
|
||||
it('returns false when command dispatch fails', function () {
|
||||
$this->commandBus->setShouldFail(true);
|
||||
|
||||
$result = $this->mailer->queue($this->message);
|
||||
|
||||
expect($result)->toBeFalse();
|
||||
});
|
||||
|
||||
it('does not use transport for queued sending', function () {
|
||||
$this->mailer->queue($this->message);
|
||||
|
||||
expect($this->transport->getSentMessageCount())->toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sendBatch method', function () {
|
||||
beforeEach(function () {
|
||||
$this->messages = [
|
||||
new Message(
|
||||
from: new Email('sender@example.com'),
|
||||
subject: 'Test 1',
|
||||
body: 'Body 1',
|
||||
to: new EmailList(new Email('recipient1@example.com'))
|
||||
),
|
||||
new Message(
|
||||
from: new Email('sender@example.com'),
|
||||
subject: 'Test 2',
|
||||
body: 'Body 2',
|
||||
to: new EmailList(new Email('recipient2@example.com'))
|
||||
),
|
||||
];
|
||||
});
|
||||
|
||||
it('sends all messages successfully', function () {
|
||||
$results = $this->mailer->sendBatch($this->messages);
|
||||
|
||||
expect($results)->toHaveCount(2);
|
||||
expect($results[0])->toBeTrue();
|
||||
expect($results[1])->toBeTrue();
|
||||
expect($this->transport->getSentMessageCount())->toBe(2);
|
||||
});
|
||||
|
||||
it('handles mixed success and failure', function () {
|
||||
// Make transport fail on second call
|
||||
$transport = new TestTransport();
|
||||
$transport->failOn(2);
|
||||
$mailer = new Mailer($transport, $this->queue, $this->commandBus);
|
||||
|
||||
$results = $mailer->sendBatch($this->messages);
|
||||
|
||||
expect($results)->toHaveCount(2);
|
||||
expect($results[0])->toBeTrue();
|
||||
expect($results[1])->toBeFalse();
|
||||
});
|
||||
|
||||
it('handles invalid messages in batch', function () {
|
||||
$mixedBatch = [
|
||||
$this->messages[0],
|
||||
'invalid-message',
|
||||
$this->messages[1],
|
||||
];
|
||||
|
||||
$results = $this->mailer->sendBatch($mixedBatch);
|
||||
|
||||
expect($results)->toHaveCount(3);
|
||||
expect($results[0])->toBeTrue();
|
||||
expect($results[1])->toBeFalse();
|
||||
expect($results[2])->toBeTrue();
|
||||
});
|
||||
});
|
||||
|
||||
describe('queueBatch method', function () {
|
||||
beforeEach(function () {
|
||||
$this->messages = [
|
||||
new Message(
|
||||
from: new Email('sender@example.com'),
|
||||
subject: 'Test 1',
|
||||
body: 'Body 1',
|
||||
to: new EmailList(new Email('recipient1@example.com'))
|
||||
),
|
||||
new Message(
|
||||
from: new Email('sender@example.com'),
|
||||
subject: 'Test 2',
|
||||
body: 'Body 2',
|
||||
to: new EmailList(new Email('recipient2@example.com'))
|
||||
),
|
||||
];
|
||||
});
|
||||
|
||||
it('dispatches SendEmailBatchCommand for valid messages', function () {
|
||||
$results = $this->mailer->queueBatch($this->messages);
|
||||
|
||||
expect($results)->toHaveCount(2);
|
||||
expect($results[0])->toBeTrue();
|
||||
expect($results[1])->toBeTrue();
|
||||
expect($this->commandBus->wasUsed())->toBeTrue();
|
||||
|
||||
$command = $this->commandBus->getLastCommand();
|
||||
expect($command)->toBeInstanceOf(SendEmailBatchCommand::class);
|
||||
expect($command->messages)->toBe($this->messages);
|
||||
expect($command->maxRetries)->toBe(3);
|
||||
expect($command->delaySeconds)->toBe(0);
|
||||
});
|
||||
|
||||
it('handles mixed valid and invalid messages', function () {
|
||||
$mixedBatch = [
|
||||
$this->messages[0],
|
||||
'invalid-message',
|
||||
$this->messages[1],
|
||||
];
|
||||
|
||||
$results = $this->mailer->queueBatch($mixedBatch);
|
||||
|
||||
expect($results)->toHaveCount(3);
|
||||
expect($results[0])->toBeTrue(); // Valid message
|
||||
expect($results[1])->toBeFalse(); // Invalid message
|
||||
expect($results[2])->toBeTrue(); // Valid message
|
||||
|
||||
$command = $this->commandBus->getLastCommand();
|
||||
expect($command->messages)->toHaveCount(2);
|
||||
});
|
||||
|
||||
it('returns early for all invalid messages', function () {
|
||||
$invalidBatch = ['invalid1', 'invalid2'];
|
||||
|
||||
$results = $this->mailer->queueBatch($invalidBatch);
|
||||
|
||||
expect($results)->toHaveCount(2);
|
||||
expect($results[0])->toBeFalse();
|
||||
expect($results[1])->toBeFalse();
|
||||
expect($this->commandBus->wasUsed())->toBeFalse();
|
||||
});
|
||||
|
||||
it('marks all valid messages as failed when batch command fails', function () {
|
||||
$this->commandBus->setShouldFail(true);
|
||||
|
||||
$results = $this->mailer->queueBatch($this->messages);
|
||||
|
||||
expect($results)->toHaveCount(2);
|
||||
expect($results[0])->toBeFalse();
|
||||
expect($results[1])->toBeFalse();
|
||||
});
|
||||
|
||||
it('uses custom retry parameters', function () {
|
||||
$this->mailer->queueBatch($this->messages, 5, 60);
|
||||
|
||||
$command = $this->commandBus->getLastCommand();
|
||||
expect($command->maxRetries)->toBe(5);
|
||||
expect($command->delaySeconds)->toBe(60);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test stubs
|
||||
class MailTestQueue implements Queue
|
||||
{
|
||||
private bool $used = false;
|
||||
|
||||
private array $jobs = [];
|
||||
|
||||
public function push(object $job): void
|
||||
{
|
||||
$this->used = true;
|
||||
$this->jobs[] = $job;
|
||||
}
|
||||
|
||||
public function pop(): ?object
|
||||
{
|
||||
return array_shift($this->jobs);
|
||||
}
|
||||
|
||||
public function wasUsed(): bool
|
||||
{
|
||||
return $this->used;
|
||||
}
|
||||
}
|
||||
|
||||
class TestCommandBus implements CommandBus
|
||||
{
|
||||
private bool $used = false;
|
||||
|
||||
private bool $shouldFail = false;
|
||||
|
||||
private ?object $lastCommand = null;
|
||||
|
||||
public function dispatch(object $command): mixed
|
||||
{
|
||||
$this->used = true;
|
||||
$this->lastCommand = $command;
|
||||
|
||||
if ($this->shouldFail) {
|
||||
throw new Exception('Command dispatch failed');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function wasUsed(): bool
|
||||
{
|
||||
return $this->used;
|
||||
}
|
||||
|
||||
public function getLastCommand(): ?object
|
||||
{
|
||||
return $this->lastCommand;
|
||||
}
|
||||
|
||||
public function setShouldFail(bool $shouldFail): void
|
||||
{
|
||||
$this->shouldFail = $shouldFail;
|
||||
}
|
||||
}
|
||||
|
||||
class TestTransport implements \App\Framework\Mail\TransportInterface
|
||||
{
|
||||
private MockTransport $mockTransport;
|
||||
|
||||
private int $failOn = 0;
|
||||
|
||||
private int $callCount = 0;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->mockTransport = new MockTransport();
|
||||
}
|
||||
|
||||
public function send(Message $message): TransportResult
|
||||
{
|
||||
$this->callCount++;
|
||||
|
||||
if ($this->failOn > 0 && $this->callCount === $this->failOn) {
|
||||
return TransportResult::failure('Simulated failure');
|
||||
}
|
||||
|
||||
return $this->mockTransport->send($message);
|
||||
}
|
||||
|
||||
public function isAvailable(): bool
|
||||
{
|
||||
return $this->mockTransport->isAvailable();
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->mockTransport->getName();
|
||||
}
|
||||
|
||||
public function getSentMessageCount(): int
|
||||
{
|
||||
return $this->mockTransport->getSentMessageCount();
|
||||
}
|
||||
|
||||
public function getLastSentMessage(): ?array
|
||||
{
|
||||
return $this->mockTransport->getLastSentMessage();
|
||||
}
|
||||
|
||||
public function failOn(int $callNumber): void
|
||||
{
|
||||
$this->failOn = $callNumber;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user