diff --git a/src/Symfony/Component/Messenger/Bridge/Amqp/Tests/Transport/AmqpExtIntegrationTest.php b/src/Symfony/Component/Messenger/Bridge/Amqp/Tests/Transport/AmqpExtIntegrationTest.php index a3a363edb34d6..c982ba1b6b6b4 100644 --- a/src/Symfony/Component/Messenger/Bridge/Amqp/Tests/Transport/AmqpExtIntegrationTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Amqp/Tests/Transport/AmqpExtIntegrationTest.php @@ -75,6 +75,10 @@ public function testItSendsAndReceivesMessages() $this->assertEmpty(iterator_to_array($receiver->get())); } + /** + * @group legacy + * ^ for now, deprecation errors are thrown during serialization. + */ public function testRetryAndDelay() { $serializer = $this->createSerializer(); diff --git a/src/Symfony/Component/Messenger/CHANGELOG.md b/src/Symfony/Component/Messenger/CHANGELOG.md index aab9a173afdb7..cd62403709e46 100644 --- a/src/Symfony/Component/Messenger/CHANGELOG.md +++ b/src/Symfony/Component/Messenger/CHANGELOG.md @@ -1,6 +1,8 @@ CHANGELOG ========= + * The `RedeliveryStamp` will no longer be populated with error data. This information is now stored in the `ErrorDetailsStamp` instead. + 5.1.0 ----- diff --git a/src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php b/src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php index a95eb1fd41763..5d91247630691 100644 --- a/src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php +++ b/src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php @@ -15,6 +15,7 @@ use Symfony\Component\Console\Helper\Dumper; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Stamp\ErrorDetailsStamp; use Symfony\Component\Messenger\Stamp\RedeliveryStamp; use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp; use Symfony\Component\Messenger\Stamp\TransportMessageIdStamp; @@ -61,7 +62,11 @@ protected function displaySingleMessage(Envelope $envelope, SymfonyStyle $io) /** @var SentToFailureTransportStamp|null $sentToFailureTransportStamp */ $sentToFailureTransportStamp = $envelope->last(SentToFailureTransportStamp::class); - $lastRedeliveryStampWithException = $this->getLastRedeliveryStampWithException($envelope); + /** @var RedeliveryStamp|null $lastRedeliveryStamp */ + $lastRedeliveryStamp = $envelope->last(RedeliveryStamp::class); + /** @var ErrorDetailsStamp|null $lastErrorDetailsStamp */ + $lastErrorDetailsStamp = $envelope->last(ErrorDetailsStamp::class); + $lastRedeliveryStampWithException = $this->getLastRedeliveryStampWithException($envelope, true); $rows = [ ['Class', \get_class($envelope->getMessage())], @@ -71,14 +76,34 @@ protected function displaySingleMessage(Envelope $envelope, SymfonyStyle $io) $rows[] = ['Message Id', $id]; } - $flattenException = null === $lastRedeliveryStampWithException ? null : $lastRedeliveryStampWithException->getFlattenException(); if (null === $sentToFailureTransportStamp) { $io->warning('Message does not appear to have been sent to this transport after failing'); } else { + $failedAt = ''; + $errorMessage = ''; + $errorCode = ''; + $errorClass = '(unknown)'; + + if (null !== $lastRedeliveryStamp) { + $failedAt = $lastRedeliveryStamp->getRedeliveredAt()->format('Y-m-d H:i:s'); + } + + if (null !== $lastErrorDetailsStamp) { + $errorMessage = $lastErrorDetailsStamp->getExceptionMessage(); + $errorCode = $lastErrorDetailsStamp->getExceptionCode(); + $errorClass = $lastErrorDetailsStamp->getExceptionClass(); + } elseif (null !== $lastRedeliveryStampWithException) { + $errorMessage = $lastRedeliveryStampWithException->getExceptionMessage(); + if (null !== $lastRedeliveryStampWithException->getFlattenException()) { + $errorClass = $lastRedeliveryStampWithException->getFlattenException()->getClass(); + } + } + $rows = array_merge($rows, [ - ['Failed at', null === $lastRedeliveryStampWithException ? '' : $lastRedeliveryStampWithException->getRedeliveredAt()->format('Y-m-d H:i:s')], - ['Error', null === $lastRedeliveryStampWithException ? '' : $lastRedeliveryStampWithException->getExceptionMessage()], - ['Error Class', null === $flattenException ? '(unknown)' : $flattenException->getClass()], + ['Failed at', $failedAt], + ['Error', $errorMessage], + ['Error Code', $errorCode], + ['Error Class', $errorClass], ['Transport', $sentToFailureTransportStamp->getOriginalReceiverName()], ]); } @@ -98,6 +123,12 @@ protected function displaySingleMessage(Envelope $envelope, SymfonyStyle $io) $dump = new Dumper($io); $io->writeln($dump($envelope->getMessage())); $io->title('Exception:'); + $flattenException = null; + if (null !== $lastErrorDetailsStamp) { + $flattenException = $lastErrorDetailsStamp->getFlattenException(); + } elseif (null !== $lastRedeliveryStampWithException) { + $flattenException = $lastRedeliveryStampWithException->getFlattenException(); + } $io->writeln(null === $flattenException ? '(no data)' : $flattenException->getTraceAsString()); } else { $io->writeln(' Re-run command with -vv to see more message & error details.'); @@ -122,6 +153,23 @@ protected function getReceiver(): ReceiverInterface protected function getLastRedeliveryStampWithException(Envelope $envelope): ?RedeliveryStamp { + // Use ErrorDetailsStamp instead if it is available + if ($envelope->last(ErrorDetailsStamp::class)) { + return null; + } + + if (null === \func_get_args()[1]) { + trigger_deprecation( + 'symfony/messenger', + '5.1', + sprintf( + 'Using the "getLastRedeliveryStampWithException" method in the "%s" class is deprecated, use the "Envelope::last(%s)" instead.', + self::class, + ErrorDetailsStamp::class + ) + ); + } + /** @var RedeliveryStamp $stamp */ foreach (array_reverse($envelope->all(RedeliveryStamp::class)) as $stamp) { if (null !== $stamp->getExceptionMessage()) { diff --git a/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php b/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php index bcb6911632152..9ac7a89c8da41 100644 --- a/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php +++ b/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php @@ -18,6 +18,8 @@ use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Messenger\Stamp\ErrorDetailsStamp; +use Symfony\Component\Messenger\Stamp\RedeliveryStamp; use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface; /** @@ -82,13 +84,16 @@ private function listMessages(SymfonyStyle $io, int $max) $rows = []; foreach ($envelopes as $envelope) { - $lastRedeliveryStampWithException = $this->getLastRedeliveryStampWithException($envelope); + /** @var RedeliveryStamp|null $lastRedeliveryStamp */ + $lastRedeliveryStamp = $envelope->last(RedeliveryStamp::class); + /** @var ErrorDetailsStamp|null $lastErrorDetailsStamp */ + $lastErrorDetailsStamp = $envelope->last(ErrorDetailsStamp::class); $rows[] = [ $this->getMessageId($envelope), \get_class($envelope->getMessage()), - null === $lastRedeliveryStampWithException ? '' : $lastRedeliveryStampWithException->getRedeliveredAt()->format('Y-m-d H:i:s'), - null === $lastRedeliveryStampWithException ? '' : $lastRedeliveryStampWithException->getExceptionMessage(), + null === $lastRedeliveryStamp ? '' : $lastRedeliveryStamp->getRedeliveredAt()->format('Y-m-d H:i:s'), + null === $lastErrorDetailsStamp ? '' : $lastErrorDetailsStamp->getExceptionMessage(), ]; } diff --git a/src/Symfony/Component/Messenger/Event/AbstractWorkerMessageEvent.php b/src/Symfony/Component/Messenger/Event/AbstractWorkerMessageEvent.php index ce444d6b3cd83..ce26fed80163d 100644 --- a/src/Symfony/Component/Messenger/Event/AbstractWorkerMessageEvent.php +++ b/src/Symfony/Component/Messenger/Event/AbstractWorkerMessageEvent.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Messenger\Event; use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Stamp\StampInterface; abstract class AbstractWorkerMessageEvent { @@ -36,4 +37,12 @@ public function getReceiverName(): string { return $this->receiverName; } + + /** + * Add stamps to the envelope. + */ + public function addStamps(StampInterface ...$stamps): void + { + $this->envelope = $this->envelope->with(...$stamps); + } } diff --git a/src/Symfony/Component/Messenger/EventListener/AddErrorDetailsStampListener.php b/src/Symfony/Component/Messenger/EventListener/AddErrorDetailsStampListener.php new file mode 100644 index 0000000000000..ef35d9c58afa2 --- /dev/null +++ b/src/Symfony/Component/Messenger/EventListener/AddErrorDetailsStampListener.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\EventListener; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent; +use Symfony\Component\Messenger\Stamp\ErrorDetailsStamp; + +final class AddErrorDetailsStampListener implements EventSubscriberInterface +{ + public function onMessageFailed(WorkerMessageFailedEvent $event): void + { + /** @var ErrorDetailsStamp|null $previousStamp */ + $previousStamp = $event->getEnvelope()->last(ErrorDetailsStamp::class); + $stamp = new ErrorDetailsStamp($event->getThrowable()); + + // Do not append duplicate information + if (null === $previousStamp || !$previousStamp->equals($stamp)) { + $event->addStamps($stamp); + } + } + + public static function getSubscribedEvents(): array + { + return [ + // must have higher priority than SendFailedMessageForRetryListener + WorkerMessageFailedEvent::class => ['onMessageFailed', 200], + ]; + } +} diff --git a/src/Symfony/Component/Messenger/EventListener/SendFailedMessageToFailureTransportListener.php b/src/Symfony/Component/Messenger/EventListener/SendFailedMessageToFailureTransportListener.php index 8c84cf7992786..729dd5fec4409 100644 --- a/src/Symfony/Component/Messenger/EventListener/SendFailedMessageToFailureTransportListener.php +++ b/src/Symfony/Component/Messenger/EventListener/SendFailedMessageToFailureTransportListener.php @@ -11,10 +11,8 @@ namespace Symfony\Component\Messenger\EventListener; use Psr\Log\LoggerInterface; -use Symfony\Component\ErrorHandler\Exception\FlattenException; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent; -use Symfony\Component\Messenger\Exception\HandlerFailedException; use Symfony\Component\Messenger\Stamp\DelayStamp; use Symfony\Component\Messenger\Stamp\RedeliveryStamp; use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp; @@ -49,16 +47,10 @@ public function onMessageFailed(WorkerMessageFailedEvent $event) return; } - $throwable = $event->getThrowable(); - if ($throwable instanceof HandlerFailedException) { - $throwable = $throwable->getNestedExceptions()[0]; - } - - $flattenedException = class_exists(FlattenException::class) ? FlattenException::createFromThrowable($throwable) : null; $envelope = $envelope->with( new SentToFailureTransportStamp($event->getReceiverName()), new DelayStamp(0), - new RedeliveryStamp(0, $throwable->getMessage(), $flattenedException) + new RedeliveryStamp(0) ); if (null !== $this->logger) { diff --git a/src/Symfony/Component/Messenger/Stamp/ErrorDetailsStamp.php b/src/Symfony/Component/Messenger/Stamp/ErrorDetailsStamp.php new file mode 100644 index 0000000000000..a6cdcfbe657ac --- /dev/null +++ b/src/Symfony/Component/Messenger/Stamp/ErrorDetailsStamp.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Stamp; + +use Symfony\Component\ErrorHandler\Exception\FlattenException; +use Symfony\Component\Messenger\Exception\HandlerFailedException; +use Throwable; + +/** + * Stamp applied when a messages fails due to an exception in the handler. + */ +final class ErrorDetailsStamp implements StampInterface +{ + /** @var string */ + private $exceptionClass; + + /** @var int|mixed */ + private $exceptionCode; + + /** @var string */ + private $exceptionMessage; + + /** @var FlattenException|null */ + private $flattenException; + + public function __construct(Throwable $throwable) + { + if ($throwable instanceof HandlerFailedException) { + $throwable = $throwable->getPrevious(); + } + + $this->exceptionClass = \get_class($throwable); + $this->exceptionCode = $throwable->getCode(); + $this->exceptionMessage = $throwable->getMessage(); + + if (class_exists(FlattenException::class)) { + $this->flattenException = FlattenException::createFromThrowable($throwable); + } + } + + public function getExceptionClass(): string + { + return $this->exceptionClass; + } + + public function getExceptionCode() + { + return $this->exceptionCode; + } + + public function getExceptionMessage(): string + { + return $this->exceptionMessage; + } + + public function getFlattenException(): ?FlattenException + { + return $this->flattenException; + } + + public function equals(self $that): bool + { + if ($this->flattenException && $that->flattenException) { + return $this->flattenException->getClass() === $that->flattenException->getClass() + && $this->flattenException->getCode() === $that->flattenException->getCode() + && $this->flattenException->getMessage() === $that->flattenException->getMessage(); + } + + return $this->exceptionClass === $that->exceptionClass + && $this->exceptionCode === $that->exceptionCode + && $this->exceptionMessage === $that->exceptionMessage; + } +} diff --git a/src/Symfony/Component/Messenger/Stamp/RedeliveryStamp.php b/src/Symfony/Component/Messenger/Stamp/RedeliveryStamp.php index 60c3898b08606..901b76c59cb45 100644 --- a/src/Symfony/Component/Messenger/Stamp/RedeliveryStamp.php +++ b/src/Symfony/Component/Messenger/Stamp/RedeliveryStamp.php @@ -27,9 +27,33 @@ final class RedeliveryStamp implements StampInterface public function __construct(int $retryCount, string $exceptionMessage = null, FlattenException $flattenException = null) { $this->retryCount = $retryCount; + $this->redeliveredAt = new \DateTimeImmutable(); + + if (null !== $exceptionMessage) { + trigger_deprecation( + 'symfony/messenger', + '5.1', + sprintf( + 'Using the "$exceptionMessage" parameter in the "%s" class is deprecated, use the "%s" class instead.', + self::class, + ErrorDetailsStamp::class + ) + ); + } $this->exceptionMessage = $exceptionMessage; + + if (null !== $flattenException) { + trigger_deprecation( + 'symfony/messenger', + '5.1', + sprintf( + 'Using the "$flattenException" parameter in the "%s" class is deprecated, use the "%s" class instead.', + self::class, + ErrorDetailsStamp::class + ) + ); + } $this->flattenException = $flattenException; - $this->redeliveredAt = new \DateTimeImmutable(); } public static function getRetryCountFromEnvelope(Envelope $envelope): int @@ -45,13 +69,39 @@ public function getRetryCount(): int return $this->retryCount; } + /** + * @deprecated since Symfony 5.1, use ErrorDetailsStamp instead. + */ public function getExceptionMessage(): ?string { + trigger_deprecation( + 'symfony/messenger', + '5.1', + sprintf( + 'Using the "getExceptionMessage()" method of the "%s" class is deprecated, use the "%s" class instead.', + self::class, + ErrorDetailsStamp::class + ) + ); + return $this->exceptionMessage; } + /** + * @deprecated since Symfony 5.1, use ErrorDetailsStamp instead. + */ public function getFlattenException(): ?FlattenException { + trigger_deprecation( + 'symfony/messenger', + '5.1', + sprintf( + 'Using the "getFlattenException()" method of the "%s" class is deprecated, use the "%s" class instead.', + self::class, + ErrorDetailsStamp::class + ) + ); + return $this->flattenException; } diff --git a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php index e6d107eeaf1d1..4fd87726895bc 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\Messenger\Command\FailedMessagesShowCommand; use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Stamp\ErrorDetailsStamp; use Symfony\Component\Messenger\Stamp\RedeliveryStamp; use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp; use Symfony\Component\Messenger\Stamp\TransportMessageIdStamp; @@ -29,11 +30,13 @@ class FailedMessagesShowCommandTest extends TestCase public function testBasicRun() { $sentToFailureStamp = new SentToFailureTransportStamp('async'); - $redeliveryStamp = new RedeliveryStamp(0, 'Things are bad!'); + $redeliveryStamp = new RedeliveryStamp(0); + $errorStamp = new ErrorDetailsStamp(new \Exception('Things are bad!', 123)); $envelope = new Envelope(new \stdClass(), [ new TransportMessageIdStamp(15), $sentToFailureStamp, $redeliveryStamp, + $errorStamp, ]); $receiver = $this->createMock(ListableReceiverInterface::class); $receiver->expects($this->once())->method('find')->with(15)->willReturn($envelope); @@ -52,7 +55,8 @@ public function testBasicRun() Message Id 15 Failed at %s Error Things are bad! - Error Class (unknown) + Error Code 123 + Error Class Exception Transport async EOF , @@ -63,36 +67,71 @@ public function testBasicRun() public function testMultipleRedeliveryFails() { $sentToFailureStamp = new SentToFailureTransportStamp('async'); - $redeliveryStamp1 = new RedeliveryStamp(0, 'Things are bad!'); + $redeliveryStamp1 = new RedeliveryStamp(0); + $errorStamp = new ErrorDetailsStamp(new \Exception('Things are bad!', 123)); $redeliveryStamp2 = new RedeliveryStamp(0); $envelope = new Envelope(new \stdClass(), [ new TransportMessageIdStamp(15), $sentToFailureStamp, $redeliveryStamp1, + $errorStamp, $redeliveryStamp2, ]); $receiver = $this->createMock(ListableReceiverInterface::class); $receiver->expects($this->once())->method('find')->with(15)->willReturn($envelope); - $command = new FailedMessagesShowCommand( 'failure_receiver', $receiver ); - $tester = new CommandTester($command); $tester->execute(['id' => 15]); + $this->assertStringContainsString(sprintf(<<getRedeliveredAt()->format('Y-m-d H:i:s')), + $tester->getDisplay(true)); + } + /** + * @group legacy + */ + public function testLegacyFallback() + { + $sentToFailureStamp = new SentToFailureTransportStamp('async'); + $redeliveryStamp = new RedeliveryStamp(0, 'Things are bad!'); + $envelope = new Envelope(new \stdClass(), [ + new TransportMessageIdStamp(15), + $sentToFailureStamp, + $redeliveryStamp, + ]); + $receiver = $this->createMock(ListableReceiverInterface::class); + $receiver->expects($this->once())->method('find')->with(15)->willReturn($envelope); + $command = new FailedMessagesShowCommand( + 'failure_receiver', + $receiver + ); + $tester = new CommandTester($command); + $tester->execute(['id' => 15]); $this->assertStringContainsString(sprintf(<<getRedeliveredAt()->format('Y-m-d H:i:s')), + $redeliveryStamp->getRedeliveredAt()->format('Y-m-d H:i:s')), $tester->getDisplay(true)); } @@ -113,11 +152,13 @@ public function testReceiverShouldBeListable() public function testListMessages() { $sentToFailureStamp = new SentToFailureTransportStamp('async'); - $redeliveryStamp = new RedeliveryStamp(0, 'Things are bad!'); + $redeliveryStamp = new RedeliveryStamp(0); + $errorStamp = new ErrorDetailsStamp(new \RuntimeException('Things are bad!')); $envelope = new Envelope(new \stdClass(), [ new TransportMessageIdStamp(15), $sentToFailureStamp, $redeliveryStamp, + $errorStamp ]); $receiver = $this->createMock(ListableReceiverInterface::class); $receiver->expects($this->once())->method('all')->with()->willReturn([$envelope]); @@ -130,7 +171,7 @@ public function testListMessages() $tester = new CommandTester($command); $tester->execute([]); $this->assertStringContainsString(sprintf(<<getRedeliveredAt()->format('Y-m-d H:i:s')), @@ -158,7 +199,8 @@ public function testListMessagesReturnsPaginatedMessages() $envelope = new Envelope(new \stdClass(), [ new TransportMessageIdStamp(15), $sentToFailureStamp, - new RedeliveryStamp(0, 'Things are bad!'), + new RedeliveryStamp(0), + new ErrorDetailsStamp(new \RuntimeException('Things are bad!')) ]); $receiver = $this->createMock(ListableReceiverInterface::class); $receiver->expects($this->once())->method('all')->with()->willReturn([$envelope]); diff --git a/src/Symfony/Component/Messenger/Tests/EventListener/AddErrorDetailsStampListenerTest.php b/src/Symfony/Component/Messenger/Tests/EventListener/AddErrorDetailsStampListenerTest.php new file mode 100644 index 0000000000000..0fb58973071a8 --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/EventListener/AddErrorDetailsStampListenerTest.php @@ -0,0 +1,56 @@ +onMessageFailed($event); + + $this->assertEquals($expectedStamp, $event->getEnvelope()->last(ErrorDetailsStamp::class)); + } + + public function testWorkerAddsNewErrorDetailsStampOnFailure() + { + $listener = new AddErrorDetailsStampListener(); + + $envelope = new Envelope(new \stdClass(), [ + new ErrorDetailsStamp(new \InvalidArgumentException('First error!')), + ]); + + $exception = new \Exception('Second error!'); + $event = new WorkerMessageFailedEvent($envelope, 'my_receiver', $exception); + $expectedStamp = new ErrorDetailsStamp($exception); + + $listener->onMessageFailed($event); + + $this->assertEquals($expectedStamp, $event->getEnvelope()->last(ErrorDetailsStamp::class)); + $this->assertCount(2, $event->getEnvelope()->all(ErrorDetailsStamp::class)); + } + + public function testWorkerDoesNotAddDuplicateErrorDetailsStampOnFailure() + { + $listener = new AddErrorDetailsStampListener(); + + $envelope = new Envelope(new \stdClass(), [new \Exception('It failed!')]); + $event = new WorkerMessageFailedEvent($envelope, 'my_receiver', new \Exception('It failed!')); + + $listener->onMessageFailed($event); + + $this->assertCount(1, $event->getEnvelope()->all(ErrorDetailsStamp::class)); + } +} diff --git a/src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageToFailureTransportListenerTest.php b/src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageToFailureTransportListenerTest.php index 1f648b83e1e35..c489ac45e33c4 100644 --- a/src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageToFailureTransportListenerTest.php +++ b/src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageToFailureTransportListenerTest.php @@ -15,8 +15,6 @@ use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent; use Symfony\Component\Messenger\EventListener\SendFailedMessageToFailureTransportListener; -use Symfony\Component\Messenger\Exception\HandlerFailedException; -use Symfony\Component\Messenger\Stamp\RedeliveryStamp; use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp; use Symfony\Component\Messenger\Transport\Sender\SenderInterface; @@ -34,11 +32,6 @@ public function testItSendsToTheFailureTransport() $this->assertNotNull($sentToFailureTransportStamp); $this->assertSame('my_receiver', $sentToFailureTransportStamp->getOriginalReceiverName()); - /** @var RedeliveryStamp $redeliveryStamp */ - $redeliveryStamp = $envelope->last(RedeliveryStamp::class); - $this->assertSame('no!', $redeliveryStamp->getExceptionMessage()); - $this->assertSame('no!', $redeliveryStamp->getFlattenException()->getMessage()); - return true; }))->willReturnArgument(0); $listener = new SendFailedMessageToFailureTransportListener($sender); @@ -50,30 +43,6 @@ public function testItSendsToTheFailureTransport() $listener->onMessageFailed($event); } - public function testItGetsNestedHandlerFailedException() - { - $sender = $this->createMock(SenderInterface::class); - $sender->expects($this->once())->method('send')->with($this->callback(function ($envelope) { - /** @var Envelope $envelope */ - /** @var RedeliveryStamp $redeliveryStamp */ - $redeliveryStamp = $envelope->last(RedeliveryStamp::class); - $this->assertNotNull($redeliveryStamp); - $this->assertSame('I am inside!', $redeliveryStamp->getExceptionMessage()); - $this->assertSame('Exception', $redeliveryStamp->getFlattenException()->getClass()); - - return true; - }))->willReturnArgument(0); - - $listener = new SendFailedMessageToFailureTransportListener($sender); - - $envelope = new Envelope(new \stdClass()); - $exception = new \Exception('I am inside!'); - $exception = new HandlerFailedException($envelope, [$exception]); - $event = new WorkerMessageFailedEvent($envelope, 'my_receiver', $exception); - - $listener->onMessageFailed($event); - } - public function testDoNothingOnRetry() { $sender = $this->createMock(SenderInterface::class); diff --git a/src/Symfony/Component/Messenger/Tests/FailureIntegrationTest.php b/src/Symfony/Component/Messenger/Tests/FailureIntegrationTest.php index 3ea7602d5237d..88e32dd845ea2 100644 --- a/src/Symfony/Component/Messenger/Tests/FailureIntegrationTest.php +++ b/src/Symfony/Component/Messenger/Tests/FailureIntegrationTest.php @@ -16,6 +16,7 @@ use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent; +use Symfony\Component\Messenger\EventListener\AddErrorDetailsStampListener; use Symfony\Component\Messenger\EventListener\SendFailedMessageForRetryListener; use Symfony\Component\Messenger\EventListener\SendFailedMessageToFailureTransportListener; use Symfony\Component\Messenger\EventListener\StopWorkerOnMessageLimitListener; @@ -27,7 +28,7 @@ use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware; use Symfony\Component\Messenger\Middleware\SendMessageMiddleware; use Symfony\Component\Messenger\Retry\MultiplierRetryStrategy; -use Symfony\Component\Messenger\Stamp\RedeliveryStamp; +use Symfony\Component\Messenger\Stamp\ErrorDetailsStamp; use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface; @@ -37,7 +38,7 @@ class FailureIntegrationTest extends TestCase { - public function testRequeMechanism() + public function testRequeueMechanism() { $transport1 = new DummyFailureTestSenderAndReceiver(); $transport2 = new DummyFailureTestSenderAndReceiver(); @@ -96,6 +97,7 @@ public function testRequeMechanism() new SendMessageMiddleware($senderLocator), new HandleMessageMiddleware($handlerLocator), ]); + $dispatcher->addSubscriber(new AddErrorDetailsStampListener()); $dispatcher->addSubscriber(new SendFailedMessageForRetryListener($locator, $retryStrategyLocator)); $dispatcher->addSubscriber(new SendFailedMessageToFailureTransportListener($failureTransport)); $dispatcher->addSubscriber(new StopWorkerOnMessageLimitListener(1)); @@ -156,10 +158,10 @@ public function testRequeMechanism() /** @var SentToFailureTransportStamp $sentToFailureStamp */ $sentToFailureStamp = $failedEnvelope->last(SentToFailureTransportStamp::class); $this->assertNotNull($sentToFailureStamp); - /** @var RedeliveryStamp $redeliveryStamp */ - $redeliveryStamp = $failedEnvelope->last(RedeliveryStamp::class); - $this->assertNotNull($redeliveryStamp); - $this->assertSame('Failure from call 2', $redeliveryStamp->getExceptionMessage()); + /** @var ErrorDetailsStamp $errorDetailsStamp */ + $errorDetailsStamp = $failedEnvelope->last(ErrorDetailsStamp::class); + $this->assertNotNull($errorDetailsStamp); + $this->assertSame('Failure from call 2', $errorDetailsStamp->getExceptionMessage()); /* * Failed message is handled, fails, and sent for a retry diff --git a/src/Symfony/Component/Messenger/Tests/Stamp/ErrorDetailsStampTest.php b/src/Symfony/Component/Messenger/Tests/Stamp/ErrorDetailsStampTest.php new file mode 100644 index 0000000000000..c6db1de147fe7 --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Stamp/ErrorDetailsStampTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests\Stamp; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ErrorHandler\Exception\FlattenException; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Exception\HandlerFailedException; +use Symfony\Component\Messenger\Stamp\ErrorDetailsStamp; + +class ErrorDetailsStampTest extends TestCase +{ + public function testGetters(): void + { + $exception = new \Exception('exception message'); + $flattenException = FlattenException::createFromThrowable($exception); + + $stamp = new ErrorDetailsStamp($exception); + + $this->assertSame(\Exception::class, $stamp->getExceptionClass()); + $this->assertSame('exception message', $stamp->getExceptionMessage()); + $this->assertEquals($flattenException, $stamp->getFlattenException()); + } + + public function testUnwrappingHandlerFailedException(): void + { + $wrappedException = new \Exception('I am inside', 123); + $envelope = new Envelope(new \stdClass()); + $exception = new HandlerFailedException($envelope, [$wrappedException]); + $flattenException = FlattenException::createFromThrowable($wrappedException); + + $stamp = new ErrorDetailsStamp($exception); + + $this->assertSame(\Exception::class, $stamp->getExceptionClass()); + $this->assertSame('I am inside', $stamp->getExceptionMessage()); + $this->assertSame(123, $stamp->getExceptionCode()); + $this->assertEquals($flattenException, $stamp->getFlattenException()); + } +} diff --git a/src/Symfony/Component/Messenger/Tests/Stamp/RedeliveryStampTest.php b/src/Symfony/Component/Messenger/Tests/Stamp/RedeliveryStampTest.php index 7fcabfc2d66f6..826f3a122e5f9 100644 --- a/src/Symfony/Component/Messenger/Tests/Stamp/RedeliveryStampTest.php +++ b/src/Symfony/Component/Messenger/Tests/Stamp/RedeliveryStampTest.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Messenger\Tests\Stamp; use PHPUnit\Framework\TestCase; -use Symfony\Component\ErrorHandler\Exception\FlattenException; use Symfony\Component\Messenger\Stamp\RedeliveryStamp; class RedeliveryStampTest extends TestCase @@ -22,15 +21,5 @@ public function testGetters() $stamp = new RedeliveryStamp(10); $this->assertSame(10, $stamp->getRetryCount()); $this->assertInstanceOf(\DateTimeInterface::class, $stamp->getRedeliveredAt()); - $this->assertNull($stamp->getExceptionMessage()); - $this->assertNull($stamp->getFlattenException()); - } - - public function testGettersPopulated() - { - $flattenException = new FlattenException(); - $stamp = new RedeliveryStamp(10, 'exception message', $flattenException); - $this->assertSame('exception message', $stamp->getExceptionMessage()); - $this->assertSame($flattenException, $stamp->getFlattenException()); } }