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 2313e1308f..24dc1a23e9 100644 --- a/src/Symfony/Component/Messenger/Bridge/Amqp/Tests/Transport/AmqpExtIntegrationTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Amqp/Tests/Transport/AmqpExtIntegrationTest.php @@ -76,6 +76,10 @@ class AmqpExtIntegrationTest extends TestCase $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 63b8d4b188..9237367f44 100644 --- a/src/Symfony/Component/Messenger/CHANGELOG.md +++ b/src/Symfony/Component/Messenger/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 5.2.0 ----- +* The `RedeliveryStamp` will no longer be populated with error data. This information is now stored in the `ErrorDetailsStamp` instead. * Added `FlattenExceptionNormalizer` to give more information about the exception on Messenger background processes. The `FlattenExceptionNormalizer` has a higher priority than `ProblemNormalizer` and it is only used when the Messenger serialization context is set. * Added factory methods to `DelayStamp`. * Removed the exception when dispatching a message with a `DispatchAfterCurrentBusStamp` and not in a context of another dispatch call diff --git a/src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php b/src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php index a95eb1fd41..f33f3037b4 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\Command\Command; 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 @@ abstract class AbstractFailedMessagesCommand extends Command /** @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,35 @@ abstract class AbstractFailedMessagesCommand extends Command $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) { + // Try reading the errorMessage for messages that are still in the queue without the new ErrorDetailStamps. + $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 +124,12 @@ abstract class AbstractFailedMessagesCommand extends Command $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 +154,23 @@ abstract class AbstractFailedMessagesCommand extends Command protected function getLastRedeliveryStampWithException(Envelope $envelope): ?RedeliveryStamp { + if (null === \func_get_args()[1]) { + trigger_deprecation( + 'symfony/messenger', + '5.2', + sprintf( + 'Using the "getLastRedeliveryStampWithException" method in the "%s" class is deprecated, use the "Envelope::last(%s)" instead.', + self::class, + ErrorDetailsStamp::class + ) + ); + } + + // Use ErrorDetailsStamp instead if it is available + if (null !== $envelope->last(ErrorDetailsStamp::class)) { + return null; + } + /** @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 0baf7a419f..bf8a72f906 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\Input\InputOption; 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,25 @@ EOF $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); + $lastRedeliveryStampWithException = $this->getLastRedeliveryStampWithException($envelope, true); + + $errorMessage = ''; + if (null !== $lastErrorDetailsStamp) { + $errorMessage = $lastErrorDetailsStamp->getExceptionMessage(); + } elseif (null !== $lastRedeliveryStampWithException) { + // Try reading the errorMessage for messages that are still in the queue without the new ErrorDetailStamps. + $errorMessage = $lastRedeliveryStampWithException->getExceptionMessage(); + } $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'), + $errorMessage, ]; } diff --git a/src/Symfony/Component/Messenger/EventListener/AddErrorDetailsStampListener.php b/src/Symfony/Component/Messenger/EventListener/AddErrorDetailsStampListener.php new file mode 100644 index 0000000000..4a9aaa1d79 --- /dev/null +++ b/src/Symfony/Component/Messenger/EventListener/AddErrorDetailsStampListener.php @@ -0,0 +1,38 @@ + + * + * 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 + { + $stamp = new ErrorDetailsStamp($event->getThrowable()); + $previousStamp = $event->getEnvelope()->last(ErrorDetailsStamp::class); + + // 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 8c84cf7992..729dd5fec4 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 @@ class SendFailedMessageToFailureTransportListener implements EventSubscriberInte 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 0000000000..e06c3adf3e --- /dev/null +++ b/src/Symfony/Component/Messenger/Stamp/ErrorDetailsStamp.php @@ -0,0 +1,86 @@ + + * + * 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 (null === $that) { + return false; + } + + 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 f38b96d55a..a633e7dbef 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, \DateTimeInterface $redeliveredAt = null) { $this->retryCount = $retryCount; - $this->exceptionMessage = $exceptionMessage; - $this->flattenException = $flattenException; $this->redeliveredAt = $redeliveredAt ?? new \DateTimeImmutable(); + + if (null !== $exceptionMessage) { + trigger_deprecation( + 'symfony/messenger', + '5.2', + 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.2', + sprintf( + 'Using the "$flattenException" parameter in the "%s" class is deprecated, use the "%s" class instead.', + self::class, + ErrorDetailsStamp::class + ) + ); + } + $this->flattenException = $flattenException; } public static function getRetryCountFromEnvelope(Envelope $envelope): int @@ -45,13 +69,39 @@ final class RedeliveryStamp implements StampInterface return $this->retryCount; } + /** + * @deprecated since Symfony 5.2, use ErrorDetailsStamp instead. + */ public function getExceptionMessage(): ?string { + trigger_deprecation( + 'symfony/messenger', + '5.2', + 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.2, use ErrorDetailsStamp instead. + */ public function getFlattenException(): ?FlattenException { + trigger_deprecation( + 'symfony/messenger', + '5.2', + 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 e6d107eeaf..884d432abd 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php @@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase; 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,8 +55,9 @@ class FailedMessagesShowCommandTest extends TestCase Message Id 15 Failed at %s Error Things are bad! - Error Class (unknown) - Transport async + Error Code 123 + Error Class Exception + Transport async EOF , $redeliveryStamp->getRedeliveredAt()->format('Y-m-d H:i:s')), @@ -63,39 +67,74 @@ EOF 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')), + $tester->getDisplay(true)); + } + public function testReceiverShouldBeListable() { $receiver = $this->createMock(ReceiverInterface::class); @@ -113,11 +152,13 @@ EOF 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 @@ EOF $tester = new CommandTester($command); $tester->execute([]); $this->assertStringContainsString(sprintf(<<getRedeliveredAt()->format('Y-m-d H:i:s')), @@ -158,7 +199,8 @@ EOF $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 0000000000..0fb5897307 --- /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 1f648b83e1..c489ac45e3 100644 --- a/src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageToFailureTransportListenerTest.php +++ b/src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageToFailureTransportListenerTest.php @@ -15,8 +15,6 @@ use PHPUnit\Framework\TestCase; 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 @@ class SendFailedMessageToFailureTransportListenerTest extends TestCase $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 @@ class SendFailedMessageToFailureTransportListenerTest extends TestCase $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 3ea7602d52..88e32dd845 100644 --- a/src/Symfony/Component/Messenger/Tests/FailureIntegrationTest.php +++ b/src/Symfony/Component/Messenger/Tests/FailureIntegrationTest.php @@ -16,6 +16,7 @@ use Psr\Container\ContainerInterface; 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\FailedMessageProcessingMiddleware; 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 @@ use Symfony\Component\Messenger\Worker; class FailureIntegrationTest extends TestCase { - public function testRequeMechanism() + public function testRequeueMechanism() { $transport1 = new DummyFailureTestSenderAndReceiver(); $transport2 = new DummyFailureTestSenderAndReceiver(); @@ -96,6 +97,7 @@ class FailureIntegrationTest extends TestCase 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 @@ class FailureIntegrationTest extends TestCase /** @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 0000000000..c6db1de147 --- /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 f8a2175e2b..fa01b64f9d 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,16 +21,6 @@ class RedeliveryStampTest extends TestCase $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()); } public function testSerialization()